在 bash 中正确列出名称中带有空格的项目

在 bash 中正确列出名称中带有空格的项目

例如我有以下文件:

test1.txt
test2.txt
test space.txt

我使用以下命令:

output=$(ls -l --time-style=long-iso | awk '{print $8, $6}')

我得到以下输出:

test1.txt 
test2.txt 
test YYYY-MM-DD

由于其名称中有空格,我得到了错误的输出。

有可能得到这样的输出吗?

"text1.txt" 
"text2.txt" 
"text space.txt"

答案1

这就是为什么你永远不要尝试解析ls.ls旨在打印到终端,而不是在脚本中使用或进行解析,还有其他工具可以做到这一点。您自己也使它变得更加复杂,因为您正在使用选项,-l --time-style=long-iso但您想要的输出只是文件名。

要将当前目录中的文件和目录列表放入变量中,最简单的方法就是使用 globs:

$ ls
 test1.txt   test2.txt  'test space.txt'
$ files=( * )
$ echo "Files: ${files[@]}"
Files: test1.txt test2.txt test space.txt
$ echo "The third file is '${files[2]}'"
The third file is 'test space.txt'

要引用文件名,请执行以下操作:

$ printf '"%s"\n' "${files[@]}"
"test1.txt"
"test2.txt"
"test space.txt"

如您所见,该$files数组保存了所有文件,每个文件都作为数组中的一个元素存储。参见这里有关 bash 数组如何工作的描述,简而言之,您可以使用列出整个数组${array[@]},并使用列出单个条目,${array[N]}其中N是 (数组的第一个元素) 和 X-1 之间的数字0,其中 X 是数组中元素的数量。在上面的例子中,我们有 3 个元素$files,因此您有${files[0]}${files[1]}${files[2]}

请注意,这种方法甚至适用于名称包含换行符 ( \n) 的文件:

$ touch 'a file with a '$'\n''newline!'
$ ls
'a file with a '$'\n''newline!'   test1.txt   test2.txt  'test space.txt'
$ files=( * )
$ echo "File is: ${files[0]}"
File is: a file with a 
newline!

现在,如果您还想保留文件的修改日期(这不是您的输出所显示的,但我认为您awk正在尝试执行此操作),您可以执行以下操作:

$ files=( * )
$ for file in "${files[@]}"; do
   date=$(stat -c '%y' -- "$file" | cut -d ' ' -f1)
   printf 'Date: %s File: "%s"\n' "$date" "$file"
done
Date: 2023-08-15 File: "a file with a 
newline!"
Date: 2023-08-15 File: "test1.txt"
Date: 2023-08-15 File: "test2.txt"
Date: 2023-08-15 File: "test space.txt"

答案2

更可靠的是,find单独使用其-printf动作就可以为您提供所需的输出ls......awk就像这样:

find ! -name . -prune -printf '%P %TY-%Tm-%Td\n'

如果需要将输出分配给参数,则数组将是最合适的,同时将输出读取为 NULL 分隔符(对于不常见的文件/目录名称(例如空格/制表符/换行符)更安全)像这样:

$ touch normalfile
$ touch 'filewith'$'\n''newline'
$ touch 'filewith'$'\t''tab'
$ touch 'filewith space'
$
$
$ readarray -d '' files < <(find ! -name . -prune -printf '%P %TY-%Tm-%Td\0')
$
$ printf '%s\n' "${files[@]}"
filewith space 2023-08-15
filewith    tab 2023-08-15
normalfile 2023-08-15
filewith
newline 2023-08-15

注意find包括隐藏文件和目录(以点开头的.filename) 在输出中,所以如果这些不是想要的,那么您可能需要将它们过滤掉。

答案3

请注意,awk 的默认分隔符是空格,因此$8当您需要完整的文件名时,您需要打印所有变量,不仅是,还有 $9 ...。

ls -l --time-style=long-iso test* | awk '{ t=index($0,$7); print substr($0,t+6), $6 }'

它将找到始终为 5 个位置长的第 7 列(时间),然后将打印该行的其余部分substr($0,t+6),后跟日期$6

--zero如果使用选项ls并将 awk 的记录分隔符设置为 NULL,则可以使其处理名称包含换行符的文件。EDIT2 :

luuk@ZES:/mnt/d/TEMP$
luuk@ZES:/mnt/d/TEMP$ touch test$'\n'test.txt
luuk@ZES:/mnt/d/TEMP$ ls -l --time-style=long-iso test*
-rwxrwxrwx 1 luuk luuk 0 2023-08-15 21:04 'test'$'\n''test.txt'
luuk@ZES:/mnt/d/TEMP$ $ ls -l --zero --time-style=long-iso -- * | awk 'BEGIN{RS="\0"}{ t=index($0,$7); print substr($0,t+6), $6 }'
test
test.txt 2023-08-16

/mnt/d/TEMP是我 Windows11 电脑上的 TEMP 目录。(临时文件的特殊位置)

但是/结论:这似乎可行,但也可能取决于其他因素,例如这是在 WSL 下测试的。因此,在使用在互联网上找到的任何东西之前,请务必进行测试...

答案4

不要使用ls,而要使用stat( man stat)。您可以以任何所需的格式输出文件信息。

相关内容