使用 head 的输出复制带有空格的文件

使用 head 的输出复制带有空格的文件

我的文件夹中有 11 个名称中带有空格的文件,我想复制最新的 10 个文件。我过去ls -t | head -n 10只能获取最新的 10 个文件。当我想在 cp 语句中使用表达式时,出现错误,指出由于名称中存在空格而无法找到文件。

例如:cp: cannot stat ‘10’: No such file or directory对于名为10 abc.pdf

cp声明:cp $(ls -t | head -n 10) ~/...

我怎样才能让它发挥作用?

答案1

如果您正在使用 Bash 并且想要一个 100% 安全的方法(我想您也想要这个方法,因为您已经通过惨痛的教训学会了必须认真处理文件名),那么您可以:

shopt -s nullglob
while IFS= read -r file; do
    file=${file#* }
    eval "file=$file"
    cp "$file" Destination
done < <(
    for f in *; do
        printf '%s %q\n' "$(date -r "$f" +'%s')" "$f"
    done | sort -rn | head -n10
)

我们来看看它的几个部分:

for f in *; do
    printf '%s %q\n' "$(date -r "$f" +'%s')" "$f"
done

这将打印到表单的标准输出条款

Timestamp Filename

其中时间戳是自纪元以来的修改日期(通过-r的选项获得date)以秒为单位,文件名是文件名的带引号版本可以重复用作 shell 输入(请参阅help printf%q格式说明符)。然后将这些行用 排序(按数字顺序,以相反的顺序)sort,并且仅保留前 10 行。

然后将其送入while循环。时间戳会随着赋值被删除file=${file# *}(这会删除第一个空格之前的所有内容),然后明显危险的行eval "file=$file"会删除 引入的转义字符printf %q,最后我们可以安全地复制文件。

可能不是最好的方法或实现,但 100% 保证任何可能的文件名的安全,并完成工作。不过,这将同样对待常规文件、目录等。如果您想限制为常规文件,请[[ -f $f ]] || continue在该for f in *; do行后面添加。此外,它不会考虑隐藏文件。如果您想要隐藏文件(当然.也不是..),请添加shopt -s dotglob.


另一种 100% Bash 解决方案是直接使用 Bash 对文件进行排序。这是使用快速排序的方法:

quicksort() {
    # quicksorts the filenames passed as positional parameters
    # wrt modification time, newest first
    # return array is quicksort_ret
    if (($#==0)); then
        quicksort_ret=()
        return
    fi
    local pivot=$1 oldest=() newest=() f
    shift
    for f; do
        if [[ $f -nt $pivot ]]; then
            newest+=( "$f" )
        else
            oldest+=( "$f" )
        fi
    done
    quicksort "${oldest[@]}"
    oldest=( "${quicksort_ret[@]}" )
    quicksort "${newest[@]}"
    quicksort_ret+=( "$pivot" "${oldest[@]}" )
}

然后,将它们整理出来,保留前 10 个,并将它们复制到您的目的地:

$ shopt -s nullglob
$ quicksort *
$ cp -- "${quicksort_ret[@]:0:10}" Destination

与之前的方法相同,这将同等对待常规文件、目录等并跳过隐藏文件。


对于另一种方法:如果您lsiq参数,您可以使用以下内容:

ls -tiq | head -n10 | cut -d ' ' -f1 | xargs -I X find -inum X -exec cp {} Destination \; -quit

这将显示文件的索引节点,并且find可以对其索引节点引用的文件执行命令。

同样,这也将处理目录等,而不仅仅是常规文件。我不太喜欢这个,因为它太依赖于ls的输出格式......

答案2

对于目录中的任何文件组,这将始终忽略最旧的文件:

less_oldest() {
    while [ -e "$1" ] ; do
        for f do [ "$f" -ot "$1" ] && break
        done && { set -- "$@" "$1"
            shift ; continue
        } ; shift ; break
    done
    printf '///%s///\n' "$@" |
        sed "s|'"'|&"&"&|g;'"s|///|'|g"
}

它是完全可移植的,并且它会打印安全的 shell 引用的文件名数组,无论它们可能返回什么字符。在您的情况下,因为您只需要 dro pone,所以您只需要运行一次,如下所示:

{   printf 'cp -t ~"/..."'
    less_oldest "${dir_of_11_files}/"*
} | sh

相关内容