我的資料夾中有 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
與先前的方法相同,這將同等對待常規文件、目錄等並跳過隱藏文件。
對於另一種方法:如果您ls
有i
和q
參數,您可以使用以下內容:
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