【问题标题】:Bash escapes tilde and wildcards but not spaceBash 转义代字号和通配符,但不转义空格
【发布时间】:2011-06-11 00:06:15
【问题描述】:

(使用 sed 在解决方案中编辑)

我有一个文件名和目录列表,包括波浪号和通配符。例如:

~/docs/file.*    
~/my docs/*.txt
...

我读取行并将它们传递给命令(例如 rsync):

while read ROW
do
   rsync $ROW /my/dest/
done < list.txt

问题在于处理文件名中的空格。如果我将 $ROW 放在这样的双引号中

rsync "$ROW" /my/dest/

当然 bash 不会转义通配符或波浪号。但如果我不使用引号,空格会中断行。

一种可能的解决方案是更改 IFS(小心:脚本比我报告的更复杂)。 另一个解决方案(感谢 Patrick Echterbruch 的修复)是预先转义空间。但是,以下代码对我不起作用:

while read ROW
do
    export NEWROW=$(echo $ROW | sed -e 's/ /\\ /g') 
    echo "Value: $NEWROW"
    ls -1d $NEWROW
done < list.txt

请注意,没有引号传递给 ls。文件“~/a b c/test.txt”存在,但我得到:

Value: ~/saver/a\ b\ c/*.txt
ls: impossibile accedere a ~/saver/a\: Nessun file o directory
ls: impossibile accedere a b\: Nessun file o directory
ls: impossibile accedere a c/*.txt: Nessun file o directory

似乎 NEWROW 作为字符串传递给 ls,所以通配符不会扩展,但空格会不断破坏文件名,尽管已转义。

任何提示? 谢谢。

【问题讨论】:

标签: bash escaping filenames space


【解决方案1】:

据我所知,您发布的 sed 脚本不会产生任何影响 - “搜索”和“替换”部分是相同的。

你能做什么可以做什么:

ROW=$(echo $ROW | sed -e "s/ /\\\\ /g")

或(更好):

ROW=$(echo $ROW | sed -e 's/ /\\ /g')

后一个示例使用单引号,因此 shell 不会对反斜杠进行特殊处理。

关于出现的第二个问题:特殊字符(如~)在程序参数中遇到时会被bash扩展。将它们存储在变量中时不会扩展它们,当将变量作为参数传递给命令时,bash 也不会检查和扩展变量的内容。

这就是rsync $NEWROW ... 不起作用的原因 - rsync 接收 $NEWROW 的未修改内容作为第一个参数,然后必须自己进行类似 shell 的扩展。

我知道的唯一解决方法是在子shell中运行命令,即:

ROW="~/a b c"
$NEWROW=$(echo $ROW | sed -e 's/ /\\ /g')
bash -c "ls -ld $f"

这将导致当前运行的 bash 提取变量内容(即 ~/a\ b\ c 并将其传递给它即将调用的第二个 shell。这个“内部”bash 将接收命令以及参数,然后当然是参数扩展。

很抱歉一开始就错过了这一点,我只关注问题的正则表达式部分。

找到另一种可能的解决方案:使用 sed 转换路径后(例如 NEWROW=$(echo $ROW | sed -e 's/ /\\ /g') 尝试:

eval ls -ld $NEWROW

或 eval rsync $NEWROW /other/path

这应该也能正常工作,不会因启动子外壳而影响性能。

//编辑:在 sed 脚本中添加了缺少的 g

//编辑:添加了路径名扩展问题的解决方法

//编辑:添加评估解决方案

【讨论】:

  • 你能在你的本地机器上运行它吗?我无法让它工作。
  • 是的,它运行得非常好。命令:ROW="my doc";回声$行; ROW=$(echo $ROW | sed -e 's/ /\\ /'); echo $ROW 返回两行,一行带有纯空格,另一行带有反斜杠转义空格。 sed --version: GNU sed 4.2 版
  • 那么我们缺少最后一个“g”来替换每个出现的空格,而不仅仅是第一次出现:'s/ /\\ /g'。现在我想知道如何跳过已经逃脱的空格,但我认为还有其他问题。
  • @EugenioR:好的,感谢您指出这一点。将其添加到答案中。
  • 谢谢你,它现在正在工作。我想知道为每一行打开一个子shell 比更改 IFS 或执行其他操作要慢多少。
【解决方案2】:

我认为您可以将要 rsync 的列表通过管道传输到单独的文件中,然后执行它,例如

while read ROW
do
  echo "rsync '$ROW' /my/dest/"
done < list.txt > rsync.txt

sh rsync.txt

【讨论】:

  • 有趣的解决方案,但可能有点慢(特别是如果扩展通配符产生大量文件)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多