是否有人有一个模板 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 具有某些扩展的通配符功能(例如 ),否则这将无法实现bash
。globstar
如果您的 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