我正在尝试在命令行上为自己创建一个小实用程序 - 对于给定的当前工作目录,我想找到最近添加的文件并将mv
其指定为参数指定的名称
我一直试图通过以下方式实现这一目标:
ls -tr | tail -n 1 | xargs -0 -J % mv % SQL_warning_2.png
...但是 bash 报告:
mv: rename Screenshot 2020-06-22 at 17.53.23.png
to SQL_warning_2.png: No such file or directory
我猜这意味着 xargs 通过 filename: 中的空格进行单独的参数,但据我所知,Screenshot 2020-06-22 at 17.53.23.png
没有任何-print0
标志。ls
如果我的一些假设不正确,请原谅 - 我对 xargs 非常不熟悉
答案1
由于您似乎假设您的文件名不包含换行符(您使用 挑选一个tail
),因此您可以仅使用
ls -tr | tail -n 1 | xargs -I % mv -- % SQL_warning_2.png
这-I %
将导致xargs
调用该实用程序一次每行读取,并%
替换为该行的文本。相反,-J %
按空格分割线:
$ echo 1 2 3 | xargs -J {} printf '"%s"\n' {}
"1"
"2"
"3"
$ echo 1 2 3 | xargs -I {} printf '"%s"\n' {}
"1 2 3"
您似乎想要重命名当前目录中最近修改的文件。可以使用 shell 进行zsh
以下操作:
mv -- *(.om[1]) SQL_warning_2.png
这将以mv
最近修改的常规文件名作为第一个参数进行调用。它是om[1]
glob 限定符,它按 mtime 时间戳(从最新到最旧)对名称列表进行排序,并选出第一个。前面的点仅选择常规文件(不选择目录等)
从bash
:
zsh -c 'mv -- *(.om[1]) SQL_warning_2.png'
答案2
我会做这样的事情:
mv -- "$(ls -t|head -n1)" new_filename
(这里假设当前目录中最新文件的名称不包含换行符)。
答案3
是的,使用-0
,xargs
需要标准输入上的 NUL 分隔列表。在这里,您将最新文件的名称部分放在最后一个换行符之后,后跟一个换行符(该换行符是由文件名添加的,ls
而不是文件名的一部分)。
该输入中没有 NUL,因此xargs
将整个输入作为一个参数传递给mv
包含该换行符的参数,因此只有当最新文件的名称确实包含一个且仅一个换行符并且它是文件名中的最后一个字符。
在这里,您需要确保ls
输出一个以 NUL 分隔的列表,而不是一个以换行符分隔的列表,但我不知道有任何ls
实现可以做到这一点。
或者您需要恢复到默认xargs
输入格式(不带-0
),其中参数由空格(其列表取决于xargs
实现和可能的区域设置)或换行符分隔,并且"..."
和'...'
和\
用于转义这些和彼此(以与相同 shell 引用运算符不同的方式)。由于某些xargs
实现尝试将其输入解释为文本,但文件名可以包含任何字节值(NUL 和 除外/
),因此您还需要在 C 语言环境中进行该处理。
export LC_ALL=C
ls -td ./* | awk -v q="'" '
{gsub(/"/, "\"\\\"\"")} # escape "
NR == 1 {printf "\"%s", $0; next}
/\// {exit} # a / means the start of the second file
{printf "\"\\\n\"%s", $0} # escape newlines
END {if (NR) print "\""}' |
xargs -J % mv % newname
正如您所看到的,xargs
可靠地使用是一件非常痛苦的事情。某些xargs
实现对参数或输入行的大小也有非常低的限制。请注意,这-J
是一个不可移植的 BSD 扩展。
使用基于行的实用程序处理任意文件名ls
也非常困难。
最好是使用zsh
它可以按修改时间对文件列表进行排序,如 @Kusalananda 所示:
mv -- *(.om[1]) newname
在 中bash
,您还可以执行以下操作:
IFS= read -rd / newest < <(ls -td ./*) && newest=${newest%.}
newest=${newest%?} # strip newline
[ -n "$newest" ] && mv "$newest" newname
(也适用于 ksh93 或 zsh)。与前面的xargs
方法一样,我们使用./*
能够判断列表中的第二个文件在哪一行开始。