所以在 bash 中,当我这样做时
echo \*
*
这似乎是正确的,因为 * 被转义并按字面意思理解。
但我无法理解,当我这样做时
echo \\*
\*
我认为第一个反斜杠转义了第二个反斜杠,因此两个反斜杠“\\”会给我一个字面上的“\”。后面的 * 带有其特殊含义。我期待着:
echo \\*
\file1 file2 file3
答案摘要:由于 \ 是按字面意思理解的,echo \*
因此其行为与 一样echo a*
,它将查找以文字“a”开头的任何文件。
跟进问题,如果我想打印出来完全一样
\file1 file2 file3
我应该使用什么命令?例如,如下所示,但我不需要空间
echo \\ *
\ file1 file2 file3
答案1
如果当前目录中没有名称以反斜杠开头的文件,这是预期的行为。重击扩展*
以匹配任何现有文件名,但是:
如果模式与任何现有文件名或路径名都不匹配,则模式字符串应保持不变。
因为没有以 开头的文件名\
,所以该模式保持原样并被echo
赋予参数\*
。这种行为常常令人困惑,而其他一些 shell(例如zsh
)则没有这种行为。你可以在 Bash 中使用以下命令更改它shopt -o failglob
,然后它会给出一个错误,zsh
并帮助您诊断问题而不是行为不当。
*
和模式字符?
可以出现在单词中的任何位置,并且前后字符按字面意思匹配。这就是为什么echo \\*
andecho \\ *
给出如此不同的输出:第一个匹配以 开头的任何内容\
(并失败),第二个输出 a \
,然后是所有文件名。
安全地获得所需输出的最直接方法可能是使用打印函数:
printf '\\'; printf "%s " *
echo *
-
在任何情况下,如果其中包含不寻常的文件名,都是不安全的\
。
答案2
尝试这个:
printf "\\%s" "$(echo *)"
解释:
printf
接受一个格式参数和零个或多个替换到格式字符串中的参数\\
格式中的含义与中相同echo \\
%s
表示采用下一个参数并将其代入结果中"$(echo *)"
意思是:执行echo *
,并将结果放入 的单个参数中printf
;由于printf
工作原理,它必须放入一个参数中
答案3
尝试...
set file*
printf %s\ "\\$@"
它将在数组的头部添加一个反斜杠 - 所以只有第一个元素。你也可以对结尾做同样的事情。您可以将它们全部用反斜杠分开,\\
例如:
printf \\%s file*
...或者...
set file*; IFS=\\; printf %s\\n "$*"
答案4
我相信问题后半部分(“我应该使用什么命令?”)的所有其他答案都是错误的,即使只是在技术上或在边缘情况下。我相信这解决了其他答案中的问题:
(set -- *; echo "\\$*")
并且(FWIW)所有写入都是在单个命令中完成的
(与printf '\\'; printf "%s " *; echo
)不同。
- 使用子 shell 可以避免在外部作用域/上下文中设置/更改位置参数。
- 天真地,
set *
将位置参数($1
、$2
、$3
等)设置为当前目录中的文件名称。 (文件名开头为.
(点)是否包含在内,取决于是否dotglob
设置了该选项。) set *
如果存在名称开头的文件,则可能会失败-
(破折号)(它是*
列表中的第一个文件)。set -- *
处理这个;--
说“这之后的所有内容都是参数,而不是选项/标志。”$*
$1 $2 $3 …
是位置参数 ( ) 的列表,参数之间用空格分隔。原因\\
会\
在行的开头输出,就像echo \\ *
(问题中的示例命令)一样,但它不会干扰后续$*
.$*
是位置参数列表 ($1 $2 $3 …
), 参数由第一个字符分隔$IFS
。如果 的第一个字符$IFS
可能不是空格,请执行
(set -- *; IFS=" "; echo "\\$*")
稍微详细一点的选项包括
(set -- *; printf "%s\n" "\\$*")
和
(set -- *; printf "\\%s\n" "$*")
echo
在即使没有选项也可能解释参数字符串中的反斜杠的环境中,这些更安全-e
。
再次,IFS=" ";
如果 的第一个字符$IFS
可能不是空格,请添加。