我怎样才能逃避一个bash循环列表的空白? [英] How can I escape white space in a bash loop list?

查看:120
本文介绍了我怎样才能逃避一个bash循环列表的空白?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有通过一定的目录下的所有子目录(但不包括文件)循环一个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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆