Bagaimana 'find -exec' memberikan nama file dengan spasi?

14

Jika saya memiliki direktori yang berisi beberapa file yang namanya memiliki spasi, mis

$ ls -1 dir1
file 1
file 2
file 3

Saya dapat dengan sukses menyalin semuanya ke direktori lain seperti ini:

$ find dir1 -mindepth 1 -exec cp -t dir2 {} +

Namun, output find dir1 -mindepth 1berisi ruang yang tidak terhindar:

$ find dir1 mindepth 1
dir1/file 1
dir1/file 3
dir1/file 3

Jika saya menggunakan print0alih-alih print, output masih berisi ruang yang tidak terhindar:

$ find dir1 mindepth 1 -print0
dir1/file 1dir1/file 2dir1/file 3

Untuk menyalin file-file ini secara manual menggunakan cp, saya harus melarikan diri dari ruang; tetapi tampaknya ini tidak perlu ketika cpagitasi berasal find, terlepas dari apakah saya menggunakan +atau \;pada akhir perintah.

Apa alasannya?

EmmaV
sumber

Jawaban:

8

The findperintah mengeksekusi perintah secara langsung. Perintah, termasuk argumen nama file, tidak akan diproses oleh shell atau apa pun yang dapat memodifikasi nama file. Sangat aman.

Anda benar bahwa tidak perlu keluar dari nama file yang diwakili oleh {}pada findbaris perintah.

findmeneruskan nama file mentah dari disk langsung ke daftar argumen internal -execperintah, dalam kasus Anda, cpperintah.

RobertL
sumber
1
Singkatnya, find..execdapat menangani nama file aneh sendiri ..
heemayl
2
Aturan pertama dari klub linux adalah jangan parse ls
Sergiy Kolodyazhnyy
5

Pertanyaannya adalah dua bagian:

  • bagaimana tidak find berhasil program panggilan menggunakan -exectanpa berlari ke masalah dengan ruang tertanam dalam nama file, dan
  • apa -print0pilihannya?

Untuk yang pertama, findmembuat panggilan sistem, sebenarnya salah satu dari sekelompok panggilan terkait yang disebut sebagai "exec" . Itu meneruskan nama file sebagai argumen langsung ke panggilan ini, yang kemudian diteruskan secara langsung (setelah membuat proses baru) tanpa kehilangan informasi tentang nama file.

POSIX findfitur +dijelaskan sebagai berikut, dalam pemikiran :

Fitur findutilitas SVR4 adalah -execterminator + primer. Ini memungkinkan nama file yang mengandung karakter khusus (terutama karakter baris baru ) untuk dikelompokkan bersama tanpa masalah yang terjadi jika nama file tersebut disalurkan ke xargs. Implementasi lain telah menambahkan cara lain untuk mengatasi masalah ini, terutama -print0primer yang menulis nama file dengan terminator null byte. Ini dianggap di sini, tetapi tidak diadopsi. Menggunakan null terminator berarti bahwa utilitas apa pun yang akan memproses -print0keluaran find harus menambahkan opsi baru untuk mengurai null terminator yang sekarang dibaca.

Itu " terutama yang -print0utama" mengacu pada GNU finddan xargsyang memecahkan masalah dengan cara yang berbeda. Ini juga didukung oleh FreeBSD finddan xargs. Jika Anda menambahkan-0 opsi (lihat halaman manual ) ke xargspanggilan, maka program itu menerima baris yang diakhiri oleh karakter "byte nol". Pada gilirannya, xargsmemanggil fungsi exec untuk melakukan tugasnya . Perbedaan utama antara fitur -print0dan -0versus +fitur adalah bahwa yang pertama melewati nama file di atas pipa, sedangkan yang terakhir tidak. Pengembang menemukan kegunaan untuk hampir semua fitur; pipa tidak terkecuali.

Kembali ke contoh OP, yang menggunakan -topsi untuk cp: yang tidak ditemukan di POSIX cp . Sebaliknya, ini adalah ekstensi (alias "fitur tidak standar") yang disediakan oleh GNU cp . The -0perpanjangan xargstidak akan memperbaiki contoh ini, tetapi ada kasus lain di mana ia dapat digunakan secara efektif-dengan mengingat bahwa ada alternatif portabel +, yang GNU findmenerima.

Thomas Dickey
sumber
-1

( Ini seharusnya komentar tapi terlalu besar. )

Bagi mereka yang suka mencoba berbagai hal:

Buat skrip yang berisi parameter posisi yang diteruskan, sebut saja list_positional_parameters.sh.

#!/bin/bash

# http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_07.html
# Try globbing patterns, e.g. "X[[:digit:]][[:digit:]]" to see what happens

if [ $# -lt 1 ]; then
   echo "Usage: $0 and then at least one parameter"
   exit 1
fi

counter=1

while (($#)); do
   echo "$counter = '$1'"
   # pop positional argument 1 off the stack of positional arguments
   shift
   (( counter++ ))
done

Jalankan finddengan itu di beberapa direktori $ dir:

find "$dir" -exec ./list_positional_parameters.sh '{}' ';' | less

Seperti yang diharapkan, hanya ada satu parameter di semua panggilan, nama file, apakah ada spasi dalam namanya atau tidak.

David Tonhofer
sumber
1
Anda juga dapat menggunakan printfsuka printf '"%s"\n' "$@"mencetak semua argumen posisi yang dikutip, untuk inspeksi visual.
Kusalananda