使用 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

相關內容