我怎样才能逃避一个bash循环列表的空白? [英] How can I escape white space in a bash loop list?
问题描述
我有通过一定的目录下的所有子目录(但不包括文件)循环一个bash shell脚本。的问题是,一些目录名包含空格。
下面是我的测试目录的内容:
$ LS -F测试
巴尔的摩/樱桃山/爱迪生/纽约/费城/ cities.txt
而code,通过目录循环:
在'发现F检验/ *型D`;做
回声$ F
DONE
下面是输出:
测试/巴尔的摩
测试/樱桃
爬坡道
测试/爱迪生
测试/新
纽约
市
测试/费城
樱桃山和纽约市被视为2个或3个独立的条目。
我试过引述的文件名,例如:
在`找到测试/ *型D F | SED -e'S / ^ / \\/'| sed的-e'S / $ / \\/'';做
回声$ F
DONE
但无济于事。
有一定是一个简单的方法来做到这一点。
下面的答案是伟大的。但是,为了使这更复杂 - 我并不总是想用在我的测试目录中列出的目录。有时候我想在目录中的名称作为命令行参数来传递,而不是
我把设置IFS查尔斯的建议,并以下列想出了:
dirlist =$ {@}
(
[[-z$ dirlist]]&放大器;&放大器; dirlist =`发现测试-mindepth 1型D`&放大器;&安培; IFS = $'\\ n'
在$ dirlist D组;做
回声$ d个
DONE
)
和除非有命令行参数(即使这些参数均以)空间这个工作得很好。例如,调用脚本是这样的: test.sh樱桃山,纽约市
产生下面的输出:
樱桃
爬坡道
新
纽约
市
首先,不要那样做。最好的方法是使用找到-exec
正确的:
#这是安全的
发现试验型ð-exec回声'{}'+
其他安全方法是使用NULL结尾的名单,不过这需要你寻找支撑 -print0
:
#这是安全的
而IFS =读-r -d''N;做
printf的'%Q \\ n'$ N
完成< ≤(发现测试-mindepth 1型ð-print0)
您也可以从填充找到一个数组,后来传递数组:
#这是安全的
声明-a myarray中
而IFS =读-r -d''N;做
myArray的+ =($ N)
完成< ≤(发现测试-mindepth 1型ð-print0)
printf的'%Q \\ n'$ {myArray的[@]}#printf的是一个例子;使用它不过你想要的
如果您发现不支持 -print0
,你的结果是不安全的话 - 如果存在名称中含有换行符的文件,希望下面不会表现(这是的,是合法的):
#这是不安全
而IFS =读-r N;做
printf的'%Q \\ n'$ N
完成< ≤(发现测试-mindepth 1型D)
如果一个未打算使用上述之一,第三种方法(低效率在时间和存储器使用方面,在读取操作的字分割前的子进程的整个输出)是使用一个不包含空格字符IFS
变量。关闭通配符(设置-f
)含水珠字符prevent字符串,例如 []
, *
或
被扩展为:?
#这是不安全的(但不太不安全比起来,没有下面的precautions)
(
只有在新行IFS = $'\\ n'#分裂
设置-f#禁止通配符
在$ N(发现测试-mindepth 1型D);做
printf的'%Q \\ n'$ N
DONE
)
最后,对于命令行参数的情况下,你应该使用数组,如果你的外壳支持他们(即它的ksh,bash或zsh中):
#这是安全的
在$ @D;做
printf的'%s的\\ n'$ D
DONE
将保持分离。请注意,引用(和使用 $ @
,而不是 $ *
)是很重要的。数组可以通过其他方式来填充为好,如水珠前pressions:
#这是安全的
项=(测试/ *)
在$ {项[@]}D;做
printf的'%s的\\ n'$ D
DONE
I have a bash shell script that loops through all child directories (but not files) of a certain directory. The problem is that some of the directory names contain spaces.
Here are the contents of my test directory:
$ls -F test
Baltimore/ Cherry Hill/ Edison/ New York City/ Philadelphia/ cities.txt
And the code that loops through the directories:
for f in `find test/* -type d`; do
echo $f
done
Here's the output:
test/Baltimore test/Cherry Hill test/Edison test/New York City test/Philadelphia
Cherry Hill and New York City are treated as 2 or 3 separate entries.
I tried quoting the filenames, like so:
for f in `find test/* -type d | sed -e 's/^/\"/' | sed -e 's/$/\"/'`; do
echo $f
done
but to no avail.
There's got to be a simple way to do this.
The answers below are great. But to make this more complicated - I don't always want to use the directories listed in my test directory. Sometimes I want to pass in the directory names as command-line parameters instead.
I took Charles' suggestion of setting the IFS and came up with the following:
dirlist="${@}"
(
[[ -z "$dirlist" ]] && dirlist=`find test -mindepth 1 -type d` && IFS=$'\n'
for d in $dirlist; do
echo $d
done
)
and this works just fine unless there are spaces in the command line arguments (even if those arguments are quoted). For example, calling the script like this: test.sh "Cherry Hill" "New York City"
produces the following output:
Cherry Hill New York City
First, don't do it that way. The best approach is to use find -exec
properly:
# this is safe
find test -type d -exec echo '{}' +
The other safe approach is to use NUL-terminated list, though this requires that your find support -print0
:
# this is safe
while IFS= read -r -d '' n; do
printf '%q\n' "$n"
done < <(find test -mindepth 1 -type d -print0)
You can also populate an array from find, and pass that array later:
# this is safe
declare -a myarray
while IFS= read -r -d '' n; do
myarray+=( "$n" )
done < <(find test -mindepth 1 -type d -print0)
printf '%q\n' "${myarray[@]}" # printf is an example; use it however you want
If your find doesn't support -print0
, your result is then unsafe -- the below will not behave as desired if files exist containing newlines in their names (which, yes, is legal):
# this is unsafe
while IFS= read -r n; do
printf '%q\n' "$n"
done < <(find test -mindepth 1 -type d)
If one isn't going to use one of the above, a third approach (less efficient in terms of both time and memory usage, as it reads the entire output of the subprocess before doing word-splitting) is to use an IFS
variable which doesn't contain the space character. Turn off globbing (set -f
) to prevent strings containing glob characters such as []
, *
or ?
from being expanded:
# this is unsafe (but less unsafe than it would be without the following precautions)
(
IFS=$'\n' # split only on newlines
set -f # disable globbing
for n in $(find test -mindepth 1 -type d); do
printf '%q\n' "$n"
done
)
Finally, for the command-line parameter case, you should be using arrays if your shell supports them (i.e. it's ksh, bash or zsh):
# this is safe
for d in "$@"; do
printf '%s\n' "$d"
done
will maintain separation. Note that the quoting (and the use of $@
rather than $*
) is important. Arrays can be populated in other ways as well, such as glob expressions:
# this is safe
entries=( test/* )
for d in "${entries[@]}"; do
printf '%s\n' "$d"
done
这篇关于我怎样才能逃避一个bash循环列表的空白?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!