file tersembunyi cp dengan pola glob

13

Situasi:

$ mkdir foo && touch foo/.test
$ cp foo/* .
zsh: no matches found: foo/*
(or bash : cp: cannot stat foo/*’: No such file or directory)

Saya memiliki direktori yang penuh dengan folder dan file tersembunyi. Apa yang terjadi dan apa solusinya?

Gradien
sumber
dotglobditerjemahkan globdotsoleh zsh yang merupakan nama opsi yang tidak valid untuk bash .

Jawaban:

19

Penafian: Jawaban ini berkaitan dengan Bash secara khusus tetapi sebagian besar berlaku untuk pertanyaan mengenai pola glob!

Karakter bintang ( *) adalah wildcard. Ada serangkaian karakter tertentu yang akan menggantikannya dan karakter pertama menjadi titik ( .) bukan salah satunya. Ini adalah kasus khusus hanya karena cara kerja sistem file Unix, file yang dimulai dengan titik dianggap "tersembunyi". Itu berarti bahwa alat-alat seperti cp, ls, dll tidak akan "melihat" mereka kecuali secara eksplisit diperintahkan untuk melakukannya.

Contohnya

Pertama mari kita buat beberapa data sampel.

$ mkdir .dotdir{1,2} regdir{1,2}
$ touch .dotfile{1,2} regfile{1..3}

Jadi sekarang kita memiliki yang berikut:

$ tree -a
.
|-- .dotdir1
|-- .dotdir2
|-- .dotfile1
|-- .dotfile2
|-- regdir1
|-- regdir2
|-- regfile1
|-- regfile2
`-- regfile3

Sekarang mari kita mainkan beberapa game. Anda dapat menggunakan perintah echountuk mencantumkan apa wildcard tertentu ( *) akan untuk perintah yang diberikan seperti:

$ echo *
regdir1 regdir2 regfile1 regfile2 regfile3


$ echo reg*
regdir1 regdir2 regfile1 regfile2 regfile3

$ echo .*
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2

$ echo .* *
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3

$ echo .dotdir*
.dotdir1 .dotdir2

Mengubah perilaku?

Anda dapat menggunakan perintah shopt -s dotglobuntuk mengubah perilaku *sehingga selain file seperti regfile1itu juga akan cocok .dotfile1.

kutipan dari bashhalaman manual

dotglob If set, bash includes filenames beginning with a `.' in the results 
        of pathname expansion.

Contoh:

$ shopt -s dotglob
$ echo *
.dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3

Anda dapat mengembalikan perilaku ini dengan perintah ini:

$ shopt -u dotglob
$ echo *
regdir1 regdir2 regfile1 regfile2 regfile3

Situasi Anda?

Untuk Anda, Anda memberi tahu cpbahwa Anda ingin menyalin semua file yang sesuai dengan pola *, dan tidak ada file.

$ cp foo/.* .

Atau Anda dapat melakukan ini jika Anda ingin semua yang ada di foofolder:

$ cp foo .

Atau Anda bisa eksplisit:

$ cp foot/.* foo/* .

Bentuk yang lebih ringkas menggunakan ekspansi brace di bash:

$ cp foo/{.,}* .

Kapan saja Anda dapat menggunakan echotrik untuk melihat apa pola file yang Anda usulkan (itulah istilah mewah untuk bagian bintang itu).

$ echo {.,}*
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2 abc regdir1 regdir2 regfile1 regfile2 regfile3

Kebetulan jika Anda akan menyalin direktori file + direktori lain, Anda biasanya ingin melakukan ini secara rekursif, itulah -Rperalihan ke cp:

$ cp -R foo/. .
slm
sumber
1
Anda mungkin ingin menyebutkan penggunaan shopt -s dotglob, echo *kemudian memberikan:.dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3
Drav Sloan
@ DravSloan - terima kasih atas sarannya. Menambahkannya.
slm
cp -r foo .biasanya tidak akan berfungsi karena cpakan menolak untuk menyalin foosendiri, mungkin maksud Anda cp -R foo/. .. (perhatikan itu cp -Ryang standar).
Stéphane Chazelas
1
Perhatikan juga bahwa (cukup masuk akal), zshtidak termasuk .dan ..dalam perluasan .*. Dalam zsh, jika suatu pola tidak cocok, perintah dibatalkan (cukup masuk akal lagi), sehingga dalam kasus umum, cp foo/{,.}* .akan gagal jika tidak ada file yang tersembunyi atau tidak ada yang tersembunyi.
Stéphane Chazelas
7
Jawaban ini hanya berlaku untuk bash. Penanya menggunakan zsh, di mana jawabannya jauh lebih sederhana dan shopttidak ada.
Gilles 'SO- stop being evil'
29

Dengan zsh, cara tipikal adalah menggunakan D kualifikasi glob (untuk memasukkan [D] file):

cp foo/*(D) .

Perhatikan bahwa dengan atau tanpa D, gumpalan zsh tidak pernah termasuk .atau ..(seperti yang Anda harapkan).

Stéphane Chazelas
sumber
4

Shell memperlakukan file-file itu sebagai tersembunyi ketika ia menyelesaikan *karakter, jadi cptidak menerima nama file ini sebagai argumen.

Anda dapat menyalinnya dengan menentukan secara eksplisit cp foo/.* .

ulangan
sumber
1

Jika yang ingin Anda lakukan adalah menyalin semua file & direktori dari satu tempat ke tempat lain, Anda dapat menggunakan rsyncperintah standar . Dalam contoh yang Anda berikan di atas:

mkdir foo && touch foo/.test
rsync -a foo/ .

akan secara rekursif menyalin semua konten foo, termasuk file tersembunyi dan direktori tersembunyi, ke direktori saat ini. Garis miring pada akhir foo/adalah penting untuk rsync; dengan itu hanya isi fooyang disalin, tanpa itu rsync akan menyalin foojuga. Sebagai contoh:-

mkdir src && mkdir dest && touch src/.test
rsync -a src  dest    // copies 'src' contents to 'dest/src'
rsync -a src/ dest    // copies 'src' contents to 'dest' 

Ada banyak opsi lain yang tersedia untuk rsync, termasuk menyalin antar mesin.

Rob Swarbrick
sumber
1

Zsh Stéphane Chazelas ini jawaban benar untuk ekspresi tunggal dengan segumpal di dalamnya. Namun, jika Anda ingin mengaturnya untuk semua ekspresi secara default, Anda dapat menggunakannya

setopt GLOB_DOTS

Masukkan ke dalam Anda ~/.zshrcuntuk membuatnya permanen.

Sparhawk
sumber