Masalah dengan spasi pada nama file

8

Saya ingin melakukan sesuatu berulang kali pada daftar file. File dalam pertanyaan memiliki spasi dalam namanya:

david@david: ls -l
total 32
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha
-rw-rw-r-- 1 david david  0 Mai  8 11:55 haha~
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (3rd copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (4th copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (5th copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (6th copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (7th copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (another copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (copy)

Sekarang saya ingin memberi stat pada masing-masing file ini:

david@david: echo '
for file in $(ls)
do
stat $file
done' | bash

(Saya menggunakan gema dan pipa untuk menulis perintah multi-line.)

Ketika saya melakukan itu, itu berfungsi dengan benar pada file-file yang tidak memiliki spasi di namanya. Tapi yang lain ...

stat: cannot stat ‘(another’: No such file or directory
stat: cannot stat ‘copy)’: No such file or directory

Mengubah $(ls)ke "$(ls)"atau $fileke "$file"tidak bekerja. Apa yang dapat saya?

Edit:

echo '
for files in *
do
stat "$files"
done' | bash

lakukan triknya! Karena saya baru mengenal bash, saya ingin menjaga hal-hal sesederhana mungkin - jadi tidak ada yang mencoba keluar dari ruang, atau menggunakan xargsatau solusi read -r, meskipun mereka memecahkan masalah.

Seperti beberapa orang bertanya: Ya, menggunakan ini bukan stat *aneh. Tapi saya hanya ingin menemukan cara umum untuk menerapkan perintah yang sama pada banyak nama file di bash, menggunakan for for. Jadi statbisa berdiri untuk gzip, gpgatau rm.

pengguna258532
sumber
1
apa yang salah dengan stat *? (;-)
Rmano
Saya hanya menggunakan stat sebagai contoh. :) Saya ingin mengumpulkan nama file dengan ls, dan kemudian menggunakan hasil ls dalam bash loop.
user258532

Jawaban:

12

Kutipan berganda dari yang echo 'memperumit masalah.

Anda bisa menggunakan:

for f in *; do stat -- "$f"; done

Tetapi juga

stat -- * 

... dan jika Anda ingin mengumpulkan file dan kemudian menerapkan perintah (mengapa?) Anda dapat pergi dengan (tetapi hati-hati dengan file yang berisi baris baru ... (1))

for f in *; do echo "$f"; done | xargs stat --

... dan jika Anda ingin file tersembunyi juga, gunakan saja * .*sebagai pola, tetapi kemudian ingat itu .dan ..akan di set .

Sebagai tambahan, Anda tidak harus mengurai lsoutput .


(1) tetapi jika Anda memiliki nama file dengan baris baru, Anda agak layak mendapatkannya ... ;-)

Rmano
sumber
"dan kemudian menerapkan perintah (mengapa?)" -> stat hanya berfungsi sebagai perintah arbitrer, karena saya mencoba cara melakukan bash loop dengan nama file. Itu bisa gpg, gzip atau apa pun.
user258532
@ user258532 apa pun perintahnya, selalu gunakan for f in *; do command "$f"; done. Tidak pernah parse ls, tentu tidak pernah melakukannya dalam satu for lingkaran dan mengapa menggunakan echo?
terdon
echo: Karena saya suka menulis perintah pada banyak baris ... :)
user258532
2
@ user258532 Hah? Mengapa Anda membutuhkan gaung untuk itu? Cukup tekan enter dan lanjutkan di baris baru. Jika Anda mengakhiri sejalan dengan kutipan terbuka atau pada salah satu do, |, &&dll, Anda dapat melanjutkan pada baris baru. Entah itu atau menggunakan heredocs. Tidak ada alasan untuk menggunakan echodan itu juga dapat menyebabkan masalah.
terdon
"Tekan saja enter dan lanjutkan ke baris baru." Aduh. :-D Sekarang IQ saya secara resmi di bawah 0 - Anda tahu, saya benar-benar baru untuk bash dan hal-hal seperti itu. Tapi saya benar-benar mendapatkan uang saya dengan R (suite statistik dan bahasa scripting).
user258532
6

Di samping catatan: Anda dapat membagi perintah panjang / rumit di beberapa baris dengan menambahkan spasi diikuti dengan garis miring terbalik dan mengenai Entersetiap kali Anda ingin mulai menulis ke baris baru, alih-alih memalsukan banyak proses dengan menggunakan echo [...] | bash; Anda juga harus menyertakan $filetanda kutip ganda, untuk mencegah agar tidak statpecah jika nama file mengandung spasi:

for file in $(ls); \
do \
stat "$file"; \
done

Masalahnya adalah bahwa $(ls)memperluas ke daftar nama file yang mengandung spasi, dan hal yang sama akan terjadi juga dengan "$(ls)".

Bahkan memecahkan masalah ini, metode ini masih akan memecah pada nama file yang mengandung garis miring terbalik dan pada nama file yang mengandung baris baru (seperti yang ditunjukkan oleh terdon).

Solusi untuk kedua masalah tersebut adalah menyalurkan output findke whileloop yang dijalankan read -rsehingga, pada setiap iterasi, read -rakan menyimpan satu baris findoutput ke $file:

find . -maxdepth 1 -type f | while read -r file; do \
    stat "$file"; \
done
kos
sumber
1
dan file tersembunyi? :)
AB
5
Itu masih akan gagal pada nama file yang mengandung baris baru. Hanya saja, jangan mengurai ls. Pernah.
terdon
4
@ user258532 tidak, tidak. Serius, jangan menguraikanls . Ada cara yang lebih baik dan lebih kuat. Anda mungkin juga ingin membaca ini: Mengapa * tidak * parse `ls`? untuk lebih jelasnya.
terdon
1
Masalahnya di sini bukan ls, itu for- foriterates atas (IFS dipisahkan) kata-kata yang diberikan setelah kata inkunci`
glenn jackman
1
@glennjackman juga ya, itu kombinasi dari fordan ls. for f in *akan baik-baik saja, misalnya.
terdon
3

Gunakan yang lama find, bekerja dengan file tersembunyi, baris baru dan spasi.

find . -print0 | xargs -I {} -0 stat {}

atau yang lain sebagai gantinya stat

find . -print0 | xargs -I {} -0 file {}
find . -print0 | xargs -I {} -0 cat {}
AB
sumber
1

Sebagai seorang pria R, saya sudah menemukan solusi di R:

filenames <- dir(); # reads file names into an array.
                    # It works also recursively
                    # with dir(recursive=TRUE)
for (i in 1:length(filenames)) {
system(     # calls a system function
 paste(     # join stat and the file name
  "stat",
  filenames[i]
 )
)
}

Saya tahu, ini gila. Saya berharap output dari lsakan lebih mudah diurai ... R dapat menangani spasi, karena dir () mengembalikan nilai karakter yang dikutip. Apa pun di antara tanda kutip maka nama file yang valid dengan spasi.

pengguna258532
sumber
3
Jangan repot-repot (tapi +1 untuk usaha! :). Cukup gunakan for f in *, tekan enter dan lanjutkan pada baris baru:, dotekan enter lagi stat "$f",, enter lagi, doneenter. Itu adalah perintah yang dibagi dengan baik pada 4 baris dan tidak akan merusak jenis nama file apa pun.
terdon
1

Saya telah mengalami contoh lain dari masalah spasi putih untuk loop, jadi perintah (imo lebih kuat) berikut adalah apa yang biasanya saya gunakan. Ini juga cocok dengan pipa.

$ ls | while read line; do stat "$line"; done;

Anda bisa menggabungkan ini dengan grepatau menggunakan find:

$ find ./ -maxdepth 2 | grep '^\./[/a-z]+$' | while read line; do stat "$line"; done;
Tyzoid
sumber
Jawaban pertama Anda gagal pada nama file yang mengandung garis miring terbalik, atau memimpin atau mengekor spasi. Jawaban kedua Anda gagal total kecuali Anda menambahkan -Eopsi grep, yang tanpanya ia tidak akan mengenali +dalam ekspresi reguler. Meski begitu, ini adalah ayunan dan rindu pada pertanyaan ini, karena Anda grep menghapus nama file yang mengandung spasi . Itu juga menghapus nama file yang mengandung digit (angka) dan tanda baca (mis., Tanda kurung), seperti contoh dalam pertanyaan. Dan itu bahkan tidak menyebutkan nama file yang berisi baris baru atau mulai dengan -(tanda hubung).
Scott
-1

Jawaban ini akan memecahkan masalah penguraian lsdan merawat backspaces dan baris baru

Coba ini, itu akan menyelesaikan masalah Anda menggunakan Internal Field Separator IFS.

IFS="\n" for f in $(ls); do   stat "$f"; done

Tetapi Anda juga dapat dengan mudah menyelesaikannya tanpa perlu mengurai output menggunakan

for f in *; do   stat "$f"; done
Maythux
sumber
1
tidak berfungsi untuk file tersembunyi.
AB
2
OP tidak meminta file tersembunyi
Maythux
1
Saya tidak mengerti mengapa Anda perlu memodifikasi di IFSsini: mengutip variabel harus cukup untuk mencegah pemisahan kata, tentunya?
steeldriver
untuk parsing ls .
Maythux
Untuk apa downvote !!!
Maythux
-1

Sebagai gantinya, Anda dapat mengganti nama file Anda menggantikan ruang dengan beberapa karakter lain seperti garis bawah, sehingga Anda menyingkirkan masalah ini:

Untuk melakukannya, jalankan perintah dengan mudah:

for file in * ; do mv "$f" "${f// /_}" ; done
Maythux
sumber