Ganti Backslash untuk Forward slash di perintah terakhir

10

Saya telah menyalin perintah cd C:\foo\bar\dari PowerShell ke Cygwin dan masih berharap untuk mengeksekusi. Saya sekarang mencoba menjalankan substitusi untuk mengganti semua \dengan /:

$ !!:gs/\\/\/
bash: :gs/\\/\/: substitution failed

Tidak yakin mengapa saya mendapatkan substitusi gagal.

Saya juga mencoba:

$ !!:gs/\\/q

Hanya untuk melihat apakah penggantian itu masalahnya. Ini bukan. Sekarang saya penasaran!

Mengapa saya mendapatkan "substitusi gagal"?

Tanner Faulkner
sumber
2
Saya tidak punya cygwin saat ini, tetapi saya pikir, jalur Windows berfungsi ... apakah Anda mencoba menghanguskan kutipan argumen? Yaitucd 'C:\foo\bar'
mpy
@ mpy Ini sebenarnya berfungsi juga! Tidak tahu saya tidak terjebak dengan substitusi.
Tanner Faulkner
1
"Akan terbuka untuk jawaban menggunakan zsh juga". !!:gs/\\/\/
Dokumen
@TannerFaulkner fc -s '\'='/' -1bekerja di bawah bash default Ubuntu LTS. Beri tahu saya jika berfungsi di bawah Cygwin juga. Saya mengirim lebih banyak kata dalam jawabannya.
Hastur
1
Saya pikir perilaku bash umum di sini adalah bahwa backslash diproses sebagai lolos sebelum substitusi terjadi !!:gs/\\/\//. @Astur fc -s '\'='/' -1mendapat perilaku yang diharapkan. ini mungkin bug di bash.
quixotic

Jawaban:

6

garis miring juga merupakan pembatas dari perintah pengganti Anda, jadi Anda harus menghindarinya sebagai string pengganti agar tidak mengakhiri substitusi lebih awal

!!:gs/\\/\//

atau lebih tepatnya seperti yang disarankan dalam komentar, dengan menggunakan sesuatu selain garis miring sebagai pembatas

!!:gs|\\|/|

Tapi ini sepertinya hanya bekerja di tcsh(shell yang biasanya ditugaskan di mana saya berada), saya tidak bisa mendapatkan perintah untuk bekerja bashkarena tampaknya melarikan diri tidak bekerja pada argumen pertama dari:s

diinfiks
sumber
Tanpa sukacita :( Kesalahan yang sama i.imgur.com/QFQR0xF.png
Tanner Faulkner
1
Hanya sebuah komentar: !!:gs|\\|/mungkin sedikit lebih mudah dibaca.
mpy
1
Saya tidak bisa membuatnya bekerja di bash. Argumen pertama dari :sdo't tampaknya menjadi regex nyata
dipasang
4

Jawaban singkat: builtin fc(perintah fix) dari bash

fcadalah perintah, built-in di shell bash, dibuat untuk mengedit & mengeksekusi kembali perintah sejarah.
Ia hadir di CygWin juga dan berfungsi pada semua distribusi Linux yang saya uji:

fc  -s '\'='/' -1

Beberapa penjelasan

  • fcadalah bash built-in perintah untuk daftar atau mengedit dan re-menjalankan perintah dari daftar sejarah.
  • -1akan mengambil perintah terakhir dalam sejarah. Perhatikan bahwa genap !!didefinisikan (dibaca dari man bash) sebagai

    !! Refer to the previous command. This is a synonym for! -1 '.

  • -suntuk mengganti pola dengan yang lain. Kali ini dari help fc(perintah bawaan begitu help):

    Dengan format `fc -s [pat = rep ...] [command] ', PERINTAH dieksekusi kembali setelah subtitusi OLD = BARU dilakukan.

    Oke maksud mereka pat=repalih-alih OLD=NEW...

  • Pola akan dibaca oleh shell sehingga kita harus melindunginya dengan kutipan: '\'dan '/'.

Beberapa kata lebih lanjut tentang mengapa Anda mendapatkan "substitusi gagal"

Tampaknya untuk s modifikator belum (belum) menerapkan substitusi karakter backslash \, itulah yang melarikan diri. Untuk memastikan kita harus melihat kode, misalnya, versi gnu dari ekspansi sejarah bash (tetapi ada perintah di atas untuk mendapatkan apa yang Anda coba lakukan ... jadi saya menganggapnya malas ....).

Beberapa catatan:

  1. Kami beranggapan bahwa itu akan berfungsi setiap RegEx kami temukan bekerja dengan sed, tetapi tidak dijamin. Garis miring terbalik adalah karakter pelarian dari ekspansi dan masalahnya ada di sini. Selain itu perilaku ekspansi terkait dengan shoptopsi, jadi kita harus mulai melihat kasus per kasus ...

  2. Ketika Anda menempelkan string cd C:\Foo\Bardalam bash shell Anda, itu akan diperluas dan akan muncul untuk penerjemah sebagai cd C:FooBar; dalam bentuk ini akan disimpan dalam $_variabel internal juga.
    Jika Anda malah menempelkan cd "C:\Foo\Bar"atau cd 'C:\Foo\Bar'dalam $_ variabel Anda harus menemukan C:\Foo\Bar.
    Karena ekspansi History dilakukan segera setelah baris lengkap dibaca, sebelum shell memecahnya menjadi kata-kata, Anda mungkin tergoda untuk mulai menggunakannya dengan sedikit bashism , misalnya, dengan beberapa turunan dari (mungkin menambah :patau :q, "", parsing dan sebagainya ...)

    !!:0 ${_//\\/\/}

    Ini adalah momen untuk mengingat bahwa tidak aman untuk mulai bermain dengan path dan nama file , terutama jika itu berasal dari clipboard windows (baca secara umum halaman Mengapa tidak parse ls?, Pada dasarnya terkait dengan kemungkinan untuk menggunakan tab, spasi dan baris baru sebagai karakter yang benar untuk nama file dan direktori ...).
    Terlebih lagi ketika Anda menempelkan teks yang diambil dengan mouse , Anda juga dapat menempelkan ruang utama. Ini dapat menghindari bahwa perintah Anda akan selesai dalam sejarah (itu tergantung dari opsi shell ...). Jika demikian, pengikut Anda !!akan menjadi perintah yang tidak terkontrol ... (lihat contoh dalam jawaban lain ).Ini adalah risiko nyata yang tidak dibutuhkan .

Kesimpulan

Ekspansi sejarah memperkenalkan kata-kata dari daftar riwayat ke dalam aliran input, membuatnya mudah untuk mengulangi perintah, menyisipkan argumen ke perintah sebelumnya ke dalam jalur input saat ini, atau memperbaiki kesalahan dalam perintah sebelumnya dengan cepat.

Jika tidak mudah saya mulai berpikir kita melakukan sesuatu yang salah ;-)


Ad mual: percobaan kecil

Saya telah mengaktifkan histverifydi shell maka ...

shopt -s histverify
echo C:\Foo\Bar
!!:s|C|D| {1,2}A

kemudian saya tekan Enterdan sebagai ekspansi terverifikasi saya temukan

echo D:\Foo\Bar {1,2}A

lalu saya tekan Enterlagi dan bergema

D:FooBar 1A 2A

Ini tampaknya menunjukkan bahwa yang substitution faileddihasilkan dalam ekspansi sejarah diproses sebelum ekspansi Brace , jadi pertama-tama , dan tampaknya mengkonfirmasi bahwa spengubah sejarah belum (belum) memproses substitusi \karakter sebagai regex nyata. ..

Cepat
sumber
2

Anda dapat menggunakan seduntuk mengganti dan menjalankan hasilnya.

Ada beberapa varian yang mungkin untuk perintah seperti itu, tetapi pada bash saya hanya yang ini berfungsi:

A=$(echo "!!\\" | sed 's,\\,/,g');eval $A

Yang \\ditambahkan ke !!adalah dalam kasus perintah terakhir diakhiri dengan garis miring terbalik. Tanpa itu, shell akan bingung dan meminta kelanjutan dari garis.

Anda juga bisa menambahkan ini sebagai fungsi bash dalam file ~/.bashrc:

function slash {
   A=$(history | tail -n 2 | head -n 1 | cut -c 8- | sed 's,\\,/,g')
   eval $A
}

Berikut ini cara kerjanya di Bash saya di Windows:

pesta

Untuk menjelaskan bagaimana A=perintah memberikan nilai yang tepat ke variabel shell A:

  • tailmengambil dua baris terakhir dari sejarah perintah, yang memberikan garis cd \mnt\cdiikuti dengan slashsendirinya. Bagian dari history | tail -n 2juga dapat ditulis sebagai history 2.
  • head ambil baris pertama yaitu cd \mnt\c
  • cut memotong nomor baris yang disediakan oleh history
  • sed mengganti semua anti-garis miring dengan garis miring
  • eval mengeksekusi isi dari variabel A
harrymc
sumber
Hai Harry. Maaf untuk mengatakan bahwa pada bash GNU saya versi 4.3.11 (1) A=$(echo "!!\\" | sed 's,\\,/,g');eval $Atidak berfungsi ketika perintah diakhiri dengan backslash. Anda dipaksa untuk menambahkan spasi, mis. Ke yang C:\Foo\Bar\ lain, seperti yang Anda katakan, shell akan menunggu kelanjutan garis. Anda juga dapat menekan enter dua kali. Dalam kasus pertama Anda mendapatkan C:/Foo/Bar /(dengan spasi sebelum /; dalam detik itu akan menghasilkan kesalahan. Fungsi berfungsi dengan baik.
Hastur
Saya menulis fungsinya karena (1) Jauh lebih mudah digunakan, dan (2) One-liner sepertinya terlalu bergantung pada implementasi.
harrymc
-1

Saya tidak berpikir Anda bisa melakukan ini. Anda perlu menyalin / menempel lagi.

Masalahnya tidak terkait dengan slash beeing juga pemisah perintah pengganti, karena perintah pengganti menerima pemisah apa pun. Masalahnya adalah tentang garis miring yang harus diganti, yang telah diperlakukan sebagai pelarian karakter yang tidak berguna di sebelah kanan mereka.

Oleh karena itu, ketika Anda memanggil perintah pengganti, backslash sudah hilang dari sumbernya. Coba panggil ulang perintah apa adanya ( !!). Alih-alih mencetak perintah yang sama persis termasuk c:\foo, itu akan mengeksekusi perintah minus backslash yang sudah diproses yang menghasilkan c:foo.

Dan jelas Anda tidak dapat menemukan atau mengganti karakter yang hilang. Mencoba melakukannya akan memunculkan kesalahan.

A. Loiseau
sumber
1
Baik. Itu akhirnya salah karena tes ini benar-benar berfungsi: echo c:\Foodiikuti oleh !!:gs,F,\\f,. Tapi Mengganti pelarian backslash itu sendiri tampaknya menjadi sakit.
A. Loiseau