![如何处理变量中的空格](https://linux22.com/image/32911/%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86%E5%8F%98%E9%87%8F%E4%B8%AD%E7%9A%84%E7%A9%BA%E6%A0%BC.png)
我正在编写一些脚本:
for x in `find ./ -name *.pdf`
do
echo pathname $x
done
我的文件名是Test1 ( Volume II)
,Test2 ( Volume II)
.我得到的回报
pathname Test1
pathname (
pathname Volume
pathname II
…
我如何让它保持为一个变量?
答案1
正如本网站上多次提到的,在 Bourne/POSIX shell 中不加引号的变量扩展(如$var
)或命令替换(如`cmd`
或$(cmd)
)(或大多数 shell 中的算术扩展(如$((11 * 11))
))是 split+glob 运算符。
的内容$var
或输出cmd
(不带尾随换行符)根据$IFS
特殊变量的当前值(默认情况下包含 SPC、TAB 和 NL 字符(以及 中的 NUL zsh
))以及该变量生成的每个单词进行分割分割受文件名生成的影响,也称为通配。
例如,如果find
输出./foo bar.pdf\n./*foo*\tbar.pdf\n
(\t
表示 TAB 和\n
NL),默认值为$IFS
,则命令替换将扩展为./foo bar.pdf\n./*foo*\tbar.pdf
(删除尾随换行符),然后拆分为./foo
, bar.pdf
, ./*foo*
, and foo.pdf
,./*foo*
其中通配符模式将扩展为许多参数,因为当前目录中存在名称包含foo
.
如果只想按换行符分割,则需要设置$IFS
为仅换行符:
IFS='
'
如果您不想扩展通配符模式,则需要使用以下命令禁用它
set -f
但请注意,换行符与文件名中的任何字符一样有效,因此更一般而言,find -print
无法可靠地对输出进行后处理。
输出如下:
./a.pdf
./b.pdf
要么表示当前目录中的a.pdf
和文件,要么表示目录中b.pdf
调用的文件。b.pdf
a.pdf\n.
一些find
实现,如 GNU find
(它起源于 GNU)有一个-print0
谓词来输出文件名,后跟 NUL 字符而不是 NL 字符。使用 standard find
,您可以得到-exec printf '%s\0' {} +
相同的结果。 NUL 是唯一不能出现在文件名中的字符。
然而,它zsh
是唯一可以在其变量中存储 NUL 字符(如字符$IFS
)的 shell,因此:
IFS=$'\0'
for i in `find ... -print0`; do
...
done
(不需要set -f
inzsh
因为zsh
在命令替换时不会进行通配)可以在其他 shell 中工作zsh
,但不能在其他 shell 中工作。
最好的、可移植的方法是调用find
您想要在这些文件上运行的命令。正如@Gnouc 所建议的:
find . -name '*.pdf' -exec the command {} \;
如果您需要涉及 shell 语句的更复杂的内容,您仍然可以执行以下操作:
find . -name '*.pdf' -exec sh -c '
for i do
something complex with "$i"
done' sh {} +
使用zsh
或bash
,您还可以执行以下操作:
find . -name '*.pdf' -print0 |
while IFS= read -r -d '' file; do
whatever with "$file"
done
但请注意,循环内的标准输入会受到影响。
zsh
find
(自 1990 年以来)通过语法将大部分功能包含在其通配功能中,您可以在其中指定任何级别的子目录((*/)#
语法或其更简单的形式)和通配限定符(它们是, , ... 中**/
的挂件) 。-type f
-mtime
-perm
find
其中的部分在2003年、2005年、2009年和2010年**/
被复制(尽管也复制了该部分)。并且它们都默认不启用它。不幸的是,请注意,和都遵循目录的符号链接(例如/ in 、或in or )。ksh93
fish
bash
tcsh
tcsh
***/
bash
fish
**
-L
-follow
find
***
zsh
tcsh
在这些 shell 中,您可以pdf
在任何级别的子目录中查找文件,而不必依赖,但请注意上面的有关 about和find
的警告,并且只有globbing 限定符。fish
bash
zsh
因此,例如,zsh
相当于:
find . -name '*.pdf' -type f -exec ls -ld {} +
将会:
ls -ld ./**/*.pdf(D.)
使用 时bash
,您必须执行以下操作:
shopt -s failglob
shopt -s globstar
files=(./**/*.pdf) &&
for i do
[ -f "$i" ] && ! [ -L "$i" ] && set -- "$i" "$@"
shift
done && ls -ld "$@"
答案2
最安全的方法是使用 globbing:
for file in *pdf; do echo pathname "$file"; done
如果您需要递归查找所有 pdf,请执行以下操作:
shopt -s globstar
for file in **/*pdf; do echo pathname "$file"; done
答案3
尝试一下这一行:
find ./ -name "*.pdf" -exec echo pathname {} \;
答案4
在花了几个小时寻找默认 FreeBSD shell (/bin/sh) 的解决方案后,我终于想出了自己的解决方案。它涉及使用 sed 销毁/创建空格。这还允许您更改循环内的变量并在外部使用它们,这与使用 while 循环不同(while 循环在 /bin/sh 中生成子 shell)。
REMOTE_FOLDER=/media/images
FILE_LIST=$(find $REMOTE_FOLDER | sed 's/ /\/\/\/\//g')
for FILE in $FILE_LIST; do
FILE=$(echo $FILE | sed 's/\/\/\/\// /g')
# do whatever you need to do with $FILE
done