The cp
utilitas akan dengan senang hati menimpa file target jika file yang sudah ada, tanpa disuruh pengguna.
Suatu fungsi yang mengimplementasikan cp
kemampuan dasar , tanpa menggunakan cp
adalah
cp () {
cat "$1" >"$2"
}
Jika Anda ingin meminta pengguna sebelum menimpa target (perhatikan bahwa mungkin tidak diinginkan untuk melakukan ini jika fungsinya dipanggil oleh shell non-interaktif):
cp () {
if [ -e "$2" ]; then
printf '"%s" exists, overwrite (y/n): ' "$2" >&2
read
case "$REPLY" in
n*|N*) return ;;
esac
fi
cat "$1" >"$2"
}
Pesan diagnostik harus pergi ke aliran kesalahan standar. Inilah yang saya lakukan printf ... >&2
.
Perhatikan bahwa kita tidak benar-benar perlu ke rm
file target karena pengalihan akan memotongnya. Jika kita memang menginginkannya rm
terlebih dahulu, maka Anda harus memeriksa apakah itu direktori, dan jika ya, letakkan file target di dalam direktori itu, seperti yang cp
akan dilakukan. Ini melakukan itu, tetapi masih tanpa eksplisit rm
:
cp () {
target="$2"
if [ -d "$target" ]; then
target="$target/$1"
fi
if [ -d "$target" ]; then
printf '"%s": is a directory\n' "$target" >&2
return 1
fi
if [ -e "$target" ]; then
printf '"%s" exists, overwrite (y/n): ' "$target" >&2
read
case "$REPLY" in
n*|N*) return ;;
esac
fi
cat "$1" >"$target"
}
Anda juga mungkin ingin memastikan bahwa sumber benar-benar ada, yang merupakan sesuatu yang cp
tidak lakukan ( cat
melakukannya juga, sehingga dapat dibiarkan keluar sepenuhnya, tentu saja, tapi hal itu akan membuat file target kosong):
cp () {
if [ ! -f "$1" ]; then
printf '"%s": no such file\n' "$1" >&2
return 1
fi
target="$2"
if [ -d "$target" ]; then
target="$target/$1"
fi
if [ -d "$target" ]; then
printf '"%s": is a directory\n' "$target" >&2
return 1
fi
if [ -e "$target" ]; then
printf '"%s" exists, overwrite (y/n): ' "$target" >&2
read
case "$REPLY" in
n*|N*) return ;;
esac
fi
cat "$1" >"$target"
}
Fungsi ini tidak menggunakan "bashism" dan harus bekerja di semua sh
shell.
Dengan sedikit penyesuaian lebih lanjut untuk mendukung banyak file sumber dan -i
bendera yang mengaktifkan prompt interaktif saat menimpa file yang ada:
cp () {
local interactive=0
# Handle the optional -i flag
case "$1" in
-i) interactive=1
shift ;;
esac
# All command line arguments (not -i)
local -a argv=( "$@" )
# The target is at the end of argv, pull it off from there
local target="${argv[-1]}"
unset argv[-1]
# Get the source file names
local -a sources=( "${argv[@]}" )
for source in "${sources[@]}"; do
# Skip source files that do not exist
if [ ! -f "$source" ]; then
printf '"%s": no such file\n' "$source" >&2
continue
fi
local _target="$target"
if [ -d "$_target" ]; then
# Target is a directory, put file inside
_target="$_target/$source"
elif (( ${#sources[@]} > 1 )); then
# More than one source, target needs to be a directory
printf '"%s": not a directory\n' "$target" >&2
return 1
fi
if [ -d "$_target" ]; then
# Target can not be overwritten, is directory
printf '"%s": is a directory\n' "$_target" >&2
continue
fi
if [ "$source" -ef "$_target" ]; then
printf '"%s" and "%s" are the same file\n' "$source" "$_target" >&2
continue
fi
if [ -e "$_target" ] && (( interactive )); then
# Prompt user for overwriting target file
printf '"%s" exists, overwrite (y/n): ' "$_target" >&2
read
case "$REPLY" in
n*|N*) continue ;;
esac
fi
cat -- "$source" >"$_target"
done
}
Kode Anda memiliki spasi buruk if [ ... ]
(butuh ruang sebelum dan sesudah [
, dan sebelum ]
). Anda juga tidak boleh mencoba mengarahkan ulang tes /dev/null
karena tes itu sendiri tidak memiliki output. Tes pertama selanjutnya harus menggunakan parameter posisi $2
, bukan string file
.
Menggunakan case ... esac
seperti yang saya lakukan, Anda menghindari huruf kecil / huruf besar respon dari pengguna yang menggunakan tr
. Dalam bash
, jika Anda tetap ingin melakukan ini, cara yang lebih murah untuk melakukannya adalah dengan menggunakan REPLY="${REPLY^^}"
(untuk huruf besar) atau REPLY="${REPLY,,}"
(untuk huruf kecil).
Jika pengguna mengatakan "ya", dengan kode Anda, fungsinya menempatkan nama file file target ke file target. Ini bukan menyalin file sumber. Seharusnya jatuh ke sedikit menyalin fungsi sebenarnya.
Bit penyalinan adalah sesuatu yang telah Anda terapkan menggunakan pipa. Pipeline digunakan untuk meneruskan data dari output dari satu perintah ke input dari perintah lain. Ini bukan sesuatu yang perlu kita lakukan di sini. Cukup memohon cat
pada file sumber dan mengarahkan hasilnya ke file target.
Hal yang sama salah dengan Anda menelepon tr
sebelumnya. read
akan mengatur nilai variabel, tetapi tidak menghasilkan output, jadi pemipaan read
ke apa pun tidak masuk akal.
Tidak diperlukan keluar secara eksplisit kecuali jika pengguna mengatakan "tidak" (atau fungsinya menemukan beberapa kondisi kesalahan seperti dalam bit kode saya, tetapi karena ini adalah fungsi yang saya gunakan return
daripada exit
).
Juga, Anda mengatakan "berfungsi", tetapi implementasi Anda adalah skrip.
Lihatlah https://www.shellcheck.net/ , ini adalah alat yang baik untuk mengidentifikasi bit skrip shell yang bermasalah.
Menggunakan cat
hanyalah salah satu cara untuk menyalin konten file. Cara lain termasuk
dd if="$1" of="$2" 2>/dev/null
- Menggunakan utilitas seperti filter apa pun yang dapat dibuat hanya untuk melewatkan data, misalnya
sed "" "$1" >"2"
atau awk '1' "$1" >"$2"
atau lainnya tr '.' '.' <"$1" >"$2"
.
- dll.
Agak sulitnya adalah membuat fungsi menyalin metadata (kepemilikan dan izin) dari sumber ke target.
Satu hal yang perlu diperhatikan adalah bahwa fungsi yang saya tulis akan berperilaku sangat berbeda dari cp
jika targetnya adalah /dev/tty
misalnya (file non-reguler).