例如我有以下文件:
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
)。您可以以任何所需的格式输出文件信息。