Bagaimana saya bisa lolos dari penawaran ganda di dalam penawaran ganda?

286

Bagaimana saya bisa menghindari tanda kutip ganda di dalam string ganda di Bash?

Misalnya, dalam skrip shell saya

#!/bin/bash

dbload="load data local infile \"'gfpoint.csv'\" into table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY \"'\n'\" IGNORE 1 LINES"

Saya tidak bisa mendapatkan ENCLOSED BY '\"'dengan kutipan ganda untuk melarikan diri dengan benar. Saya tidak dapat menggunakan tanda kutip tunggal untuk variabel saya, karena saya ingin menggunakan variabel $dbtable.

Sean Nguyen
sumber
1
Lihat juga mywiki.wooledge.org/BashFAQ/050
Charles Duffy
2
@kenorb Tidak terlihat seperti duplikat dari pertanyaan itu ...
Tandai
@ Ketidaktaatan Ini bukan jenis perintah yang Anda harapkan dapat diakses oleh pengguna akhir. Script pemuatan massal biasanya dijalankan di server oleh pengguna tepercaya (seperti admin sistem atau pengembang). Ya, jika pengguna akhir mengontrol nilai $dbtable, ada risiko. Ini akan sangat tidak biasa, karena pengguna akhir biasanya tidak SSH ke mesin untuk memuat data mereka.
jpmc26

Jawaban:

285

Gunakan garis miring terbalik:

echo "\""     # Prints one " character.
Peter
sumber
9
Tidak bekerja x=ls; if [ -f "$(which "\""$x"\"")" ]; then echo exists; else echo broken; fi;memberi rusak sementara ... [ -f "$(which $x)" ]; ...atau ... [ -f $(which "$x") ]; ...bekerja dengan baik. Masalah akan muncul ketika salah satu $xatau hasil $(which "$x")memberikan apa pun dengan spasi atau karakter khusus lainnya. Penanganan masalah menggunakan variabel untuk mempertahankan hasil which, tetapi apakah bash benar-benar tidak mampu menghindari penawaran atau apakah saya melakukan sesuatu yang salah?
Luc
Saya mencoba menggunakan yang berikut ini grep -oh "\"\""$counter"\""\w*" sebagai bagian dari sintaks bash di mana $counteradalah variabel. itu tidak suka pikiran
Jay D
82

Contoh sederhana untuk keluar dari kutipan di shell:

$ echo 'abc'\''abc'
abc'abc
$ echo "abc"\""abc"
abc"abc

Ini dilakukan dengan menyelesaikan yang sudah dibuka ( '), menempatkan yang lolos ( \'), dan kemudian membuka yang lain ( ').

Kalau tidak:

$ echo 'abc'"'"'abc'
abc'abc
$ echo "abc"'"'"abc"
abc"abc

Ini dilakukan dengan menyelesaikan yang sudah dibuka satu ( '), menempatkan kutipan di kutipan lain ( "'"), dan kemudian membuka yang lain ( ').

Lebih banyak contoh: Melarikan diri dari tanda kutip tunggal di dalam string kutipan tunggal

kenorb
sumber
1
Saya mencoba sh -c "echo '{" key ":" value "}'" dan bahkan sh -c "echo '{' '"' 'key' '"' ':' '"' 'value' '"' '}' "dalam upaya untuk melampirkan kata kunci dan nilai dalam tanda kutip ganda, tetapi dalam kedua kasus saya mendapat {key: value}
Igor Yagolnitser
1
Ini tampaknya tidak perlu rumit untuk kutipan ganda: echo "abc\"abc"cukup untuk menghasilkan abc"abcseperti pada jawaban Peter.
divenex
2
Dalam contoh sederhana ini memang, tetapi dalam kasus kompleks kutipan bersarang, dapat diperlukan untuk melakukan ini dan contoh @ kenorb membantu saya mencari cara untuk menangani kasus-kasus itu.
prosoitos
63

Saya tidak tahu mengapa masalah lama ini muncul hari ini di daftar yang ditandai Bash, tetapi untuk berjaga-jaga bagi para peneliti di masa depan, ingatlah bahwa Anda dapat menghindari melarikan diri dengan menggunakan kode ASCII dari karakter yang harus Anda gema.

Contoh:

 echo -e "This is \x22\x27\x22\x27\x22text\x22\x27\x22\x27\x22"
 This is "'"'"text"'"'"

\x22adalah kode ASCII (dalam hex) untuk tanda kutip ganda dan \x27untuk tanda kutip tunggal. Demikian pula Anda dapat menggemakan karakter apa pun.

Saya kira jika kita mencoba untuk menggemakan string di atas dengan backslash, kita akan membutuhkan dua baris berantakan backslashed ... :)

Untuk penugasan variabel, ini setara dengan:

 $ a=$'This is \x22text\x22'
 $ echo "$a"
 This is "text"

Jika variabel sudah ditetapkan oleh program lain, Anda masih bisa menerapkan tanda kutip ganda / tunggal dengan sed atau alat serupa.

Contoh:

 $ b="Just another text here"
 $ echo "$b"
 Just another text here
 $ sed 's/text/"'\0'"/' <<<"$b" #\0 is a special sed operator
 Just another "0" here #this is not what i wanted to be
 $ sed 's/text/\x22\x27\0\x27\x22/' <<<"$b"
 Just another "'text'" here #now we are talking. You would normally need a dozen of backslashes to achieve the same result in the normal way.
George Vasiliou
sumber
1
+1 karena itu memecahkan masalah menambahkan variabel PS1 ke ~ / .profile echo 'export PS1='\[\033[00;31m\]${?##0}$([ $? -ne 0 ] && echo \x22 \x22)\[\033[00;32m\]\u\[\033[00m\]@\[\033[00;36m\]\h\[\033[00m\][\[\033[01;33m\]\d \t\[\033[00m\]] \[\033[01;34m\]\w\n\[\033[00m\]$( [ ${EUID} -ne 0 ] && echo \x22$\x22 || echo \x22#\x22 ) '' >> ~/.profile
Yassine ElBadaoui
1
Inilah jawabannya! Aku mencintaimu, Tuan.
Miguel Rojas Cortés
28

Bash memungkinkan Anda untuk menempatkan string secara berdekatan, dan mereka hanya akan terpaku bersama.

Jadi ini:

$ echo "Hello"', world!'

menghasilkan

Hello, world!

Caranya adalah dengan mengganti antara string tunggal dan ganda seperti yang diminta. Sayangnya, dengan cepat menjadi sangat berantakan. Sebagai contoh:

$ echo "I like to use" '"double quotes"' "sometimes"

menghasilkan

I like to use "double quotes" sometimes

Dalam contoh Anda, saya akan melakukannya seperti ini:

$ dbtable=example
$ dbload='load data local infile "'"'gfpoint.csv'"'" into '"table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '"'"'"' LINES "'TERMINATED BY "'"'\n'"'" IGNORE 1 LINES'
$ echo $dbload

yang menghasilkan output berikut:

load data local infile "'gfpoint.csv'" into table example FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY "'\n'" IGNORE 1 LINES

Sulit untuk melihat apa yang terjadi di sini, tetapi saya dapat menjelaskannya menggunakan kutipan Unicode. Berikut ini tidak akan berfungsi dalam bash - itu hanya untuk ilustrasi:

dbload=' load data local infile "' “ 'gfpoint.csv'' " into' table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '' "' ' LINES' TERMINATED BY "' '\n'' " IGNORE 1 LINES' ' ' ' ' ' ' ' ' ' '

Kutipan seperti "''" di atas akan ditafsirkan oleh bash. Kutipan seperti " 'akan berakhir pada variabel yang dihasilkan.

Jika saya memberikan perlakuan yang sama dengan contoh sebelumnya, sepertinya ini:

$ echoI like to use" ' "double quotes"' " sometimes"

Kumbang
sumber
4
Oof ... itu beberapa hal jelek.
Magic Gurita Guci
17

Lihat printf ...

#!/bin/bash
mystr="say \"hi\""

Tanpa menggunakan printf

echo -e $mystr

Output: say "hai"

Menggunakan printf

echo -e $(printf '%q' $mystr)

Output: say \ "hai \"

Danny Hong
sumber
2
Perhatikan bahwa printflolos lebih banyak karakter juga, seperti ', (dan)
David Pärsson
printf %qmenghasilkan string yang siap untuk eval, tidak diformat untuk echo -e.
Charles Duffy
2
Tidak ada alasan untuk membungkusnya printfdengan penggunaan yang tidak bergunaecho . Kedua contoh Anda telah salah mengutip. Perbaikan yang tepat adalah dengan memberi tanda kutip ganda variabel.
tripleee
15

Simpan karakter kutipan ganda sebagai variabel:

dqt = '"'
echo "Kutipan ganda $ {dqt} X $ {dqt} di dalam string yang dikutip ganda"

Keluaran:

Kutipan ganda "X" di dalam string yang dikutip ganda
12 pengunci
sumber
39
Bash benar-benar bahasa terburuk
Andy Ray
@ 12oclocker, jawaban Anda sangat mudah: D! khususnya ketika menggunakan dengan perintah "sed" itu menyelamatkan hari saya!
Artanis Zeratul
11

Manfaatkan $ "string".

Dalam contoh ini, itu akan menjadi,

dbload = $ "memuat data lokal infile \" 'gfpoint.csv' \ "ke dalam tabel $ dbtable BIDANG YANG DIAKTIFKAN OLEH ',' DITUTUP OLEH '\' 'LINES DITENTUKAN OLEH \"' \ n '\ "SELESAI 1 LINES"

Catatan (dari halaman manual ):

String yang dikutip ganda didahului dengan tanda dolar ($ "string") akan menyebabkan string diterjemahkan sesuai dengan lokal saat ini. Jika lokal saat ini adalah C atau POSIX, tanda dolar diabaikan. Jika string diterjemahkan dan diganti, penggantiannya dikutip ganda.

Trisha Chatterjee
sumber
3
Bagus, tidak tahu yang itu.
David Kierans
-5

Tambahkan "\"sebelum kutipan ganda untuk menghindarinya, bukan\

#! /bin/csh -f

set dbtable = balabala

set dbload = "load data local infile "\""'gfpoint.csv'"\"" into table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '"\""' LINES TERMINATED BY "\""'\n'"\"" IGNORE 1 LINES"

echo $dbload
# load data local infile "'gfpoint.csv'" into table balabala FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY "''" IGNORE 1 LINES
Shilv
sumber
6
Downvote: Mengapa Anda memposting cshjawaban untuk sebuah bashpertanyaan? Keduanya sangat berbeda dan tidak kompatibel.
tripleee