shell 带有管道的ls命令在传递到while循环时不起作用

3ks5zfa0  于 2022-11-16  发布在  Shell
关注(0)|答案(3)|浏览(194)

我尝试执行此命令:

#!/bin/sh
cmd="ls *.txt | sed s/.txt//g"
for i in `$cmd`
do
    echo $i
    echo "Hello world"
done

也就是说,在当前工作目录中获取扩展名为.txt的内容,并从文件中删除.txt部分,然后打印出来。但是,我收到了此错误,似乎ls正在解释“|“和“sed”和“s/.txt//g”作为文本文件名:

ls: s/.txt//g: No such file or directory
ls: sed: No such file or directory
ls: |: No such file or directory

如果我将命令传递为for i in ls *.txt | sed s/.txt//g,这不是问题。

roqulrg3

roqulrg31#

有几点:

  • 如果希望这是一个bash脚本,而不是sh脚本,则需要更改shebang以调用bash。
  • BashFAQ #50中描述了计算$cmd的行为与运行具有cmd内容的命令的行为不同的直接原因。
  • 另请参阅BashFAQ #48,说明eval为何容易造成安全性风险。
  • shell有它自己的内置字符串替换行为;没有理由使用sed。请参见the bash-hackers' wiki page on parameter expansion,它描述了${var%suffix}如何扩展为删除suffix后的$var的内容。
  • ls *.txt中,不是ls计算*.txt glob:* shell本身会在启动ls可执行文件之前执行此操作 *。因此,根本没有必要运行ls:shell的内置功能生成了文件名列表,因此您最好使用该列表。
#!/usr/bin/env bash
for i in *.txt; do i=${i%.txt}
    echo "$i"
    echo "Hello world"
done
djmepvbi

djmepvbi2#

试试这个

#!/bin/sh

for i in $(ls *.txt | sed s/.txt//g)
do
    echo $i
    echo "Hello world"
done

简单而有效。

z18hc3ub

z18hc3ub3#

基本问题是扩展的顺序(请参阅man bash中的EXPANSION):
展开的顺序为:大括号扩展、代字号扩展、参数、变量和算术扩展以及命令替换(以从左到右方式进行)、单词拆分和路径名扩展。
换句话说,命令替换已经假定原始行被拆分为单个命令,并且它将不识别“|“、”;“和其他bash关键字。
如果命令是常量,简单的解决方案是内联它:

for i in `ls *.txt | sed s/.txt//g`

从OP看,命令必须是变量。在这种情况下,可以使用“eval”强制将变量重新解析到管道中:

for i in `eval $cmd`

请务必阅读所有关于使用“eval”和动态构造命令的警告。
次要注解:在sed中,命令's/.txt//g'假定.txt是正则表达式。因此,它会将btxt.txt更改为.txt。请确保对'.'进行转义,以便将其视为文字。

cmd="ls *.txt | sed -e 's/\.txt//g'

相关问题