解析ls
的输出以遍历文件列表bad。那么我应该如何按照它们首次创建的顺序迭代文件列表?我在这里浏览了几个问题,他们似乎都在解析ls
。
嵌入式链接表明:
如果你想要一些特定的排序,事情变得更加困难 只有
ls
可以执行,例如按mtime
排序。如果你想要最老的或 目录中的最新文件,请勿使用ls -t | head -1
- 阅读Bash常见问题解答 99代替。 如果您确实需要目录中所有文件的列表 按顺序由mtime按顺序处理它们,切换到 perl,让你的perl程序打开自己的目录 排序。然后在perl程序中进行处理,或者 - 最坏的情况 场景 - 让perl程序用NUL吐出文件名 分隔符。更好的是,将修改时间放在YYYYMMDD的文件名中 格式,所以glob顺序也是mtime顺序。那你就不需要了 或perl或任何东西。 (绝大多数人都想要的情况 目录中最旧或最新的文件可以通过执行来解决 此。)
这是否意味着在bash
中有没有原生方式吗?我没有权限修改文件名以包含时间。我需要在cron
中安排一个每5分钟运行一次的脚本,生成一个数组,其中包含按创建时间排序的特定目录中的所有文件,并对文件名执行一些操作并将它们移动到另一个位置。
以下工作但只是因为我没有有趣的文件名。这些文件是由服务器创建的,因此它永远不会有特殊字符,空格,换行符等。
files=( $(ls -1tr) )
我可以编写一个perl
脚本来完成我需要的操作,但如果有人能在bash
中建议正确的方法,我将不胜感激。便携式选项会很棒,但使用最新GNU实用程序的解决方案也不会成为问题。
答案 0 :(得分:6)
sorthelper=();
for file in *; do
# We need something that can easily be sorted.
# Here, we use "<date><filename>".
# Note that this works with any special characters in filenames
sorthelper+=("$(stat -n -f "%Sm%N" -t "%Y%m%d%H%M%S" -- "$file")"); # Mac OS X only
# or
sorthelper+=("$(stat --printf "%Y %n" -- "$file")"); # Linux only
done;
sorted=();
while read -d $'\0' elem; do
# this strips away the first 14 characters (<date>)
sorted+=("${elem:14}");
done < <(printf '%s\0' "${sorthelper[@]}" | sort -z)
for file in "${sorted[@]}"; do
# do your stuff...
echo "$file";
done;
除了sort
和stat
之外,所有命令都是实际的本机Bash命令(内置)*。如果你真的想要,你可以implement your own sort
using Bash builtins only,但我认为没办法摆脱stat
。
重要的部分是read -d $'\0'
,printf '%s\0'
和sort -z
。所有这些命令都与其null分隔符选项一起使用,这意味着可以安全地处理任何文件名。此外,在"$file"
和"${anarray[*]}"
中使用双引号也很重要。
* 许多人认为GNU工具在某种程度上是Bash的一部分,但从技术上讲他们并非如此。因此,stat
和sort
与perl
一样非原生。
答案 1 :(得分:2)
您可以尝试使用stat
命令管道sort
:
stat -c '%Y %n' * | sort -t ' ' -nk1 | cut -d ' ' -f2-
更新:要使用换行符处理文件名,我们可以在%N
中使用stat
格式,而不是cut
我们可以使用awk
这样:
LANG=C stat -c '%Y^A%N' *| sort -t '^A' -nk1| awk -F '^A' '{print substr($2,2,length($2)-2)}'
LANG=C
以确保stat
仅在引用文件名时使用单引号。^A
使用 Control V A 键一起输入conrtrol-A
字符。答案 2 :(得分:2)
所有警告和警告反对使用ls
解析目录,尽管如此,我们都发现自己处于这种情况。如果您确实发现自己需要排序目录输入,那么ls
最简洁地用于提供循环是ls -opts | read -r name; do...
这将处理文件名中的空格等。无需重置{{1}由于IFS
本身的性质。例如:
read
所以要寻找避免使用ls -1rt | while read -r fname; do # where '1' is ONE not little 'L'
的清洁解决方案,但如果推动推进,ls
可以谨慎使用,不会让天空掉落或者龙会拔出你的眼睛。
让我添加免责声明,让每个人都满意。如果您喜欢文件名中的ls -opts
,那么不要使用newlines
来填充循环。如果您的文件名中没有ls
,则没有其他不良副作用。
newlines
SO用户似乎不知道 contra 的用途是什么 - 请在downvoting之前查阅。
答案 3 :(得分:1)
GNU find
+ sed
+ sort
的解决方案怎么样?
只要文件名中没有换行符,就可以使用:
find . -type f -printf '%T@ %p\n' | sort -k 1nr | sed 's/^[^ ]* //'
答案 4 :(得分:1)
每个文件都有三个时间戳:
两者都不代表文件创建的时间,该信息不会保存在任何地方。在文件创建时,所有三个时间戳都被初始化,然后在读取或写入文件时,或者文件的权限被编码,或者创建或销毁硬链接时,每个时间戳都会得到适当的更新。
因此,您无法根据文件创建时间真正列出文件,因为文件创建时间不会保存在任何地方。最接近的匹配是inode修改时间。
有关如何在atime中列出文件的详细信息,请参阅ls(1) man page中-t
,-u
,-c
和-r
选项的说明,mtime或ctime命令。
答案 5 :(得分:1)
确保安装它可能需要做多一些工作(虽然可能已经安装了),但使用zsh
代替bash
这个脚本很有意义。文件名通配功能更丰富,同时仍然使用类似sh
的语言。
files=( *(oc) )
将创建一个数组,其条目是当前目录中的所有文件名,但按更改时间排序。 (使用大写O来反转排序顺序)。这将包括目录,但您可以将匹配限制为常规文件(类似于-type f
谓词到find
):
files=( *(.oc) )
find
脚本中需要 zsh
,因为它的大多数用途都包含在各种可用的glob标志和限定符中。
答案 6 :(得分:1)
我刚刚找到了使用bash
和ls
(GNU)的方法。
假设您要遍历按修改时间(-t
)排序的文件名:
while read -r fname; do
fname=${fname:1:((${#fname}-2))} # remove the leading and trailing "
fname=${fname//\\\"/\"} # removed the \ before any embedded "
fname=$(echo -e "$fname") # interpret the escaped characters
file "$fname" # replace (YOU) `file` with anything
done < <(ls -At --quoting-style=c)
鉴于某些带有特殊字符的文件名,这是ls
输出:
$ ls -A
filename with spaces .hidden_filename filename?with_a_tab filename?with_a_newline filename_"with_double_quotes"
$ ls -At --quoting-style=c
".hidden_filename" " filename with spaces " "filename_\"with_double_quotes\"" "filename\nwith_a_newline" "filename\twith_a_tab"
所以你必须处理一些文件名以获得实际的文件名。回顾:
${fname:1:((${#fname}-2))} # remove the leading and trailing "
# ".hidden_filename" -> .hidden_filename
${fname//\\\"/\"} # removed the \ before any embedded "
# filename_\"with_double_quotes\" -> filename_"with_double_quotes"
$(echo -e "$fname") # interpret the escaped characters
# filename\twith_a_tab -> filename with_a_tab
$ ./script.sh
.hidden_filename: empty
filename with spaces : empty
filename_"with_double_quotes": empty
filename
with_a_newline: empty
filename with_a_tab: empty
如图所示,file
(或您想要的命令)可以很好地解释每个文件名。
答案 7 :(得分:0)
这是使用stat
和关联数组的方法。
n=0
declare -A arr
for file in *; do
# modified=$(stat -f "%m" "$file") # For use with BSD/OS X
modified=$(stat -c "%Y" "$file") # For use with GNU/Linux
# Ensure stat timestamp is unique
if [[ $modified == *"${!arr[@]}"* ]]; then
modified=${modified}.$n
((n++))
fi
arr[$modified]="$file"
done
files=()
for index in $(IFS=$'\n'; echo "${!arr[*]}" | sort -n); do
files+=("${arr[$index]}")
done
由于sort
对行进行排序,$(IFS=$'\n'; echo "${!arr[*]}" | sort -n)
确保通过将子shell中的字段分隔符设置为换行符来对关联数组的索引进行排序。
arr[$modified]="${file}"
和files+=("${arr[$index]}")
的引用可确保保留带有新内容等警告的文件名。