甚至有可能正确处理所有可能的文件名吗?

har*_*ald 0 linux bash shell-script filenames

在 linux 中,文件名“斜杠”和“空字符”中只禁止使用两个字符。因此,每种脚本语言中的每个具有特殊含义的字符都应该转义,但文件名中也允许每个转义序列!更糟糕的是 ie bash 一些转义方法只能转义一些字符,因此要转义大量不同的字符,您应该一起使用几种不同的转义方法,但它们会相互干扰!更糟糕的是,某些命令使用某些字符来达到目的,而其他命令使用其他字符,因此对于文件的每一个简单操作,您都应该以不同的方式转义文件名!更糟糕的是,只能使用空字符来安全地分隔文件名,但大多数命令无法使用它。更糟糕的是,在 linux 中基本上一切都是文件......

所以告诉我我错在哪里......甚至有可能正确处理所有可能的文件名吗?

澄清。本来我想:

  1. 列出给定路径下的文件和文件夹

  2. 搜索列表以查找与给定条件(年龄或文件模式或大小)匹配的文件

  3. 将匹配的文件和文件夹移动到类别,即电影 由于测试的复杂性,不可能(或实际)在一个命令中完成它,所以我不得不在不同的命令之间传递文件名。由于文件名中的空格,Bash globbing 是第一件事。Globbing 总是将带有空格的文件名拆分为列表的两个元素。然后我尝试使用“查找”。这更好,但要慢得多,而且难以使用。

我不能使用任何特殊字符来转义文件名,因为我不知道文件名中可能包含什么字符。经过一些测试,我发现任何字符出现都是时间问题。

我已经尝试过定义过滤器,例如: audio_ext=(*.mp3 *.wav *.ogg *.mid *.mod *.stm *.s3m *.it *.wma *.669 *.ac3) 很快我意识到这种方式我无法为多种用途定义过滤器,因为通配符会踢掉rigths。所以我禁用了 globbing 和 history by set -fH。在没有通配符的情况下,我必须手动进行扩展

while IFS= read -r -d $'\0'; do list+=("$REPLY") done < <( find . -maxdepth 1 -mindepth 1 ${params[@]} -print0 2>/dev/null )

params数组"-iname" "*.mp3" "-o" "-iname" "*.wav"等在哪里。这一直有效,直到文件名称中有“(”。查找有关错误用法的返回错误。

说实话......直到最近 15 年来,我一直使用批处理脚本来完成这项任务。花在写作上的时间大约是一两个下午。它有缺点和!文件名问题,但通常它有效。现在我已经尝试了将近两个月的时间来用 bash 编写它。它丑陋、复杂、漏洞百出,而且似乎永远不会奏效。

gle*_*man 8

简单的。使用通配符来选择你想要的文件,并引用保存文件名的变量:

shopt -s nullglob
for file in ./*.txt; do
    do_something_with "$file"
done
Run Code Online (Sandbox Code Playgroud)

这就是它的全部内容。

更多细节:


更新: globbing不对您看到的分词效果负责。未能引用变量是。

您可以通过以下方式获取您的条件的文件信息 stat

read size mtime < <(stat -c "%s %Y" "$file")
[[ $size -gt 1000 ]] && echo "too big"
[[ $mtime -lt $(date -d yesterday +%s) ]] && echo "too old"
Run Code Online (Sandbox Code Playgroud)

更新 2:创建一个包含许多特殊字符的文件名需要混合各种引用机制,但仍然可以对该文件执行任何操作。

$ filename='~ASDFzxcv!@#$%^&*()_+[]\{}|;:",.<>?`'"'"$' \a\t\n\r\f'".txt"
#          ^^ single quoted part ^^^^^^^^^^^^^^^^   
#                             double quoted part ^^^
#                                ANSI-C quoted part ^^^^^^^^^^^^^^

$ echo "$filename"
~ASDFzxcv!@#$%^&*()_+[]\{}|;:",.<>?`'   

.txt

$ printf "%q\n" "$filename"
$'~ASDFzxcv!@#$%^&*()_+[]\\{}|;:",.<>?`\' \a\t\n\r\f.txt'

$ date > "$filename"

$ cat "$filename"
Thu Apr 12 15:14:29 EDT 2018

$ ls -lt
total 3836
-rw-rw-r-- 1 jackman jackman      29 Apr 12 15:14 ~ASDFzxcv!@#$%^&*()_+[]\{}|;:",.<>?`' ?????.txt
                ?

$ ls -lt --show-control-chars
total 3836
-rw-rw-r-- 1 jackman jackman      29 Apr 12 15:14 ~ASDFzxcv!@#$%^&*()_+[]\{}|;:",.<>?`'     

.txt
                ?
Run Code Online (Sandbox Code Playgroud)

如果 的输出ls被重定向到终端以外的任何东西(例如,文件或管道),它将--show-control-chars默认使用该样式。您可以通过运行看到这一点ls -lt | cat。  ls有其他显示选项;例如,。--quoting-style=WORD

  • @harvald 嗯......是的。 (4认同)
  • 我想我们完全理解。我认为你没有引用你的变量。阅读 [忘记在 bash/POSIX shell 中引用变量的安全隐患](https://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable-in-bash -posix-shells) (4认同)
  • @harvald 它会起作用。我承诺。 (2认同)
  • 使用 for 循环,我会在答案的顶部显示。如果你有一个特定的目录:`for file in "$dir"/*; 做...`。确保引用 `"$file"` *EVERYWHERE*。 (2认同)
  • 向我们展示您现在的做法,以便我们纠正任何错误。 (2认同)