循环执行 `ls` 生成 bash shell 脚本

循环执行 `ls` 生成 bash shell 脚本

是否有人有一个模板 shell 脚本,用于ls对目录名称列表执行某些操作并循环遍历每个目录并执行某些操作?

我打算ls -1d */获取目录名称列表。

答案1

编辑后,不使用 ls 来代替 glob,正如@shawn-j-goff 和其他人所建议的那样。

只需使用for..do..done循环:

for f in *; do
  echo "File -> $f"
done

您可以将 替换**.txt或任何其他返回列表(文件、目录或任何内容)的 glob、生成列表的命令(例如),$(cat filelist.txt)或实际上将其替换为列表。

do循环中,您只需引用带有美元符号前缀的循环变量($f如上例所示)。您可以echo对其进行任何操作,或者对其进行其他任何操作。

例如,将.xml当前目录中的所有文件重命名为.txt

for x in *.xml; do 
  t=$(echo $x | sed 's/\.xml$/.txt/'); 
  mv $x $t && echo "moved $x -> $t"
done

或者更好的是,如果您使用 Bash,您可以使用 Bash 参数扩展,而不是生成子 shell:

for x in *.xml; do 
  t=${x%.xml}.txt
  mv $x $t && echo "moved $x -> $t"
done

答案2

使用输出来ls获取文件名不是一个好主意。这可能会导致脚本出现故障甚至危险。这是因为文件名可以包含除/null字符之外的任何字符,并且ls不使用这些字符作为分隔符,因此如果文件名中有空格或换行符,则将要得到意想不到的结果。

有两种非常好的方法可以迭代文件。这里,我仅使用它们echo来演示如何对文件名进行操作;不过,您可以使用任何方法。

首先是使用 shell 本身的通配符特性。

for dir in */; do
  echo "$dir"
done

shell 扩展*/为循环读取的单独参数for;即使文件名中有空格、换行符或任何其他奇怪的字符,for也会将每个完整名称视为原子单位;它不会以任何方式解析列表。

如果您想要递归进入子目录,那么除非您的 shell 具有某些扩展的通配符功能(例如 ),否则这将无法实现bashglobstar如果您的 shell 没有这些功能,或者您想要确保您的脚本能够在各种系统上运行,那么下一个选项是使用find

find . -type d -exec echo '{}' \;

在这里,find命令将调用echo并向其传递文件名参数。它对找到的每个文件执行一次此操作。与上一个示例一样,没有解析文件名列表;相反,将文件名完全作为参数发送。

参数的语法-exec看起来有点奇怪。find将第一个参数-exec视为要运行的程序,并将每个后续参数作为传递给该程序的参数。有两个特殊参数需要-exec查看。第一个是{};此参数将替换为前面部分find生成的文件名。第二个是;,它让我们find知道这是传递给程序的参数列表的末尾;find需要这个是因为您可以继续使用更多旨在用于find和不旨在用于执行的程序的参数。之所以需要这个\,是因为shell 也会;特殊处理——它代表命令的结束,所以我们需要对其进行转义,以便shell 将其作为参数传递给find而不是自己使用它;让shell 不特殊对待它的另一种方法是将其放在引号中:对于这个目的';'同样有效\;

答案3

对于带有空格的文件,您必须确保引用变量,例如:

 for i in $(ls); do echo "$i"; done; 

或者,您可以更改输入字段分隔符(IFS)环境变量:

 (IFS=$'\n';for i in $(ls); do echo $i; done) # subshell to restore IFS

最后,根据您正在做的事情,您甚至可能不需要 ls:

 for i in *; do echo "$i"; done;

答案4

仅补充 CoverosGene 的答案,这里有一种仅列出目录名称的方法:

for f in */; do
  echo "Directory -> $f"
done

相关内容