关于bash:将字符串中的空格保留为命令行参数

Preserving whitespaces in a string as a command line argument

我在这里面临一个小问题,我想将一个包含空格的字符串传递给另一个程序,以便将整个字符串视为命令行参数。

简而言之,我想通过bash shell脚本执行以下结构的命令:
command_name -a arg1 -b arg2 -c"此处带有空格的arg"

但是无论如何尝试,空格都不会保留在字符串中,并且默认情况下会被标记化。 请解决

编辑:这是我脚本的主要部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash

#-------- BLACKRAY CONFIG ---------------#
# Make sure the current user is in the sudoers list
# Running all instances with sudo

BLACKRAY_BIN_PATH='/opt/blackray/bin'
BLACKRAY_LOADER_DEF_PATH='/home/crozzfire'
BLACKRAY_LOADER_DEF_NAME='load.xml'
BLACKRAY_CSV_PATH='/home/crozzfire'
BLACKRAY_END_POINT='default -p 8890'
OUT_FILE='/tmp/out.log'

echo"The current binary path is $BLACKRAY_BIN_PATH"


# Starting the blackray 0.9.0 server
sudo"$BLACKRAY_BIN_PATH/blackray_start"

# Starting the blackray loader utility
BLACKRAY_INDEX_CMD="$BLACKRAY_BIN_PATH/blackray_loader -c $BLACKRAY_LOADER_DEF_PATH/$BLACKRAY_LOADER_DEF_NAME -d $BLACKRAY_CSV_PATH -e""$BLACKRAY_END_POINT"""

sudo time $BLACKRAY_INDEX_CMD -a $OUT_FILE

#--------- END BLACKRAY CONFIG ---------#


之所以遇到这个问题,是因为您将命令存储在变量中,然后再将其展开。除非有充分的理由这样做,否则不要:

1
sudo time $BLACKRAY_BIN_PATH/blackray_loader -c $BLACKRAY_LOADER_DEF_PATH/$BLACKRAY_LOADER_DEF_NAME -d $BLACKRAY_CSV_PATH -e"$BLACKRAY_END_POINT" -a $OUT_FILE

如果确实需要存储命令并在以后使用它,则有几个选项;例如, bash-hackers.org Wiki上有一个很好的主题页面。在我看来,这里最有用的方法是将命令放入数组而不是简单的变量中:

1
2
3
BLACKRAY_INDEX_CMD=($BLACKRAY_BIN_PATH/blackray_loader -c $BLACKRAY_LOADER_DEF_PATH/$BLACKRAY_LOADER_DEF_NAME -d $BLACKRAY_CSV_PATH -e"$BLACKRAY_END_POINT")

sudo time"${BLACKRAY_INDEX_CMD[@]}" -a $OUT_FILE

这样可以避免空格分隔的单词和空格内的单词之间的整体混乱,因为单词不是由空格分隔的-它们位于数组的单独元素中。使用后缀[@]将双引号引起来的数组扩展会保留该结构。

(顺便说一句,另一种选择是使用转义引号,就像您正在做的那样,然后使用eval运行命令。不要这样做;这是引入奇怪的解析错误的好方法。)


我有一个建议:

1
2
3
4
5
6
7
8
# iterate through the passed arguments, save them to new properly quoted ARGS string
while [ -n"$1" ]; do
   ARGS="$ARGS '$1'"
   shift
done

# invoke the command with properly quoted arguments
my_command $ARGS


编辑:

尝试:

1
BLACKRAY_END_POINT="'default -p 8890'"

要么

1
BLACKRAY_END_POINT='"default -p 8890"'

要么

1
BLACKRAY_END_POINT="default\ -p\ 8890"

要么

1
BLACKRAY_END_POINT='default\ -p\ 8890'

1
BLACKRAY_INDEX_CMD="$BLACKRAY_BIN_PATH/blackray_loader -c $BLACKRAY_LOADER_DEF_PATH/$BLACKRAY_LOADER_DEF_NAME -d $BLACKRAY_CSV_PATH -e $BLACKRAY_END_POINT"

原始答案:

blackray_loader是shell脚本吗?

这是一个说明,您在指定参数和处理参数时都必须处理此问题:

名为" test.txt"的文本文件(包括行号):

1
2
3
4
1 two words
2 two        words
3 two
4 words

一个名为" spacetest"的脚本:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
echo"No quotes in script"
echo $1
grep $1 test.txt
echo

echo"With quotes in script"
echo"$1"
grep"$1" test.txt
echo

./spacetest"two--------words"运行它(用空格替换连字符):

1
2
3
4
5
6
7
8
9
10
No quotes in script
two words
grep: words: No such file or directory
test.txt:1 two words
test.txt:2 two        words
test.txt:3 two

With quotes in script
two        words
2 two        words

您可以看到,在"无引号"部分中,它尝试执行grep two words test.txt,该解释将"单词"解释为文件名,而不是" test.txt"。此外,echo删除了多余的空格。

如第二节所述,将参数加引号时,grep将其视为一个参数(包括多余的空格)并正确处理。并且echo保留了多余的空格。

顺便说一下,我使用了多余的空间只是为了帮助演示。


可能需要用双引号将参数引起来(例如" $ {6}")。

在OP评论后应为" $ BLACKRAY_END_POINT"


以下是我通过exec su USER或exec su-USER重新启动脚本的示例。它可以容纳:

  • 从相对路径或当前工作目录中调用
  • 脚本名称和参数中的空格
  • 参数中的单引号和双引号,没有疯狂的转义,例如:
1
2
3
4
5
6
7
8
#
# This script should always be run-as a specific user
#
user=jimbob
if [ $(whoami) !="$user" ]; then
  exec su -c"'$(readlink -f"$0")' $(printf" %q""$@")" - $user
  exit $?
fi

其他博客上的帖子为我解决了这个空白问题:http://logbuffer.wordpress.com/2010/09/23/bash-scripting-preserve-whiteserve-in-variables/

默认情况下,空格被修剪:

1
2
3
4
bash> VAR1="abc        def    gh ijk"
bash> echo $VAR1
abc def gh ijk
bash>

"造成这种现象的原因是内部外壳变量$ IFS(内部字段分隔符),默认为空格,制表符和换行符。
要保留所有连续的空格,您必须将IFS设置为其他值"

使用IFS旁路:

1
2
3
4
5
bash> IFS='%'
bash> echo $VAR1
abc        def    gh ijk
bash>unset IFS
bash>

对于我的命令情况,它的工作非常出色:

1
su - user1 -c 'test -r"'${filepath}'"; ....'

希望这可以帮助。