Jak uciec od znaków specjalnych w ciągu?

16

Załóżmy, że $filetrzymasz wartość nazwy pliku, powiedzmy Dr' A.tif. W programowaniu bash, jak mogę uniknąć pojedynczego cudzysłowu i innych znaków specjalnych $filebez usuwania znaku specjalnego?

Aktualizacja w dniu 9 lipca 2014 r

Na żądanie @Gilles następujący fragment kodu, który nie jest w stanie obsłużyć Dr' A.tif:

files=$(find /path/ -maxdepth 1 -name "*.[Pp][Dd][Ff]" -o -name "*.[Tt][Ii][Ff]")
echo "${files}" > ${TEMP_FILE}
while read file
do
   newfile=$(echo "${file}" | sed 's, ,\\ ,g') ## line 1
done < ${TEMP_FILE}

Po Próbowałem się na odpowiedź od @Patrick na line 1, wydaje się pracować dla mnie. Ale jeśli mam taki plik Dr\^s A.tif, printfpolecenie nie wydaje się pomocne, pokazuje mi Dr\^s\ A.tif. Jeśli ręcznie wypróbuję to na konsoli:

printf "%q" "Dr\^s A.tif"

Będę miał ten wynik:

Dr\\\^s\ A.tif

Masz pomysł, jak sobie z tym poradzić?

huahsin68
źródło
3
w jakim kontekście zamierzasz użyć pliku $? lub czy ten problem przypisuje ciąg znaków ze znakiem specjalnym do pliku $?
okradać
Właściwie polecenie find zwróci mi tablicę listy plików. A potem przeglądam listę plików do zmiennej $ file.
huahsin68
2
Otrzymałeś kilka poprawnych odpowiedzi, ale prawdopodobnie nie są to odpowiedzi na pytanie, które naprawdę zadajesz. Po twoim komentarzu tutaj mocno podejrzewam, że jesteś na niewłaściwym torze. Nie możemy ci pomóc, ponieważ wciąż nie wyświetlasz swojego kodu. Zalecam jednak przeczytanie Dlaczego mój skrypt powłoki dusi się na białych znakach lub innych znakach specjalnych? jako tło. Pokaż skrypt i wyjaśnij, co chcesz zrobić.
Gilles 'SO - przestań być zły'

Odpowiedzi:

14

Aby to zrobić, możesz użyć printfwbudowanego %q. Na przykład:

$ file="Dr' A.tif"
$ printf '%q\n' "$file"
Dr\'\ A.tif

$ file=' foo$bar\baz`'
$ printf '%q\n' "$file"
\ foo\$bar\\baz\`

Z dokumentacji bash na printf:

In addition to the standard format specifications described in printf(1)
and printf(3), printf interprets:

 %b       expand backslash escape sequences in the corresponding argument
 %q       quote the argument in a way that can be reused as shell input
 %(fmt)T  output the date-time string resulting from using FMT as a format
          string for strftime(3)
Patrick
źródło
Nie działa to w niektórych przypadkach .eg, gdy trzeba zachować podwójne cudzysłowy.
user3467349,
@ user3467349 działa dobrze z cytatami. Założę się, że próbujesz czegoś takiego printf '%s' "foo". Najpierw musisz zrozumieć, jak parsowanie argumentów działa w powłoce. Zobacz gnu.org/software/bash/manual/bash.html#Shell-Operation # 2 dzieje się przed # 6.
Patrick,
Czy możesz podać przykład tego ciągu wydrukowanego bez printfżadnych innych manipulacjiIt doesn't have a: ""
3467349
Twój problem nie ma z tym nic wspólnego printf. Problem polega na tym, że nie chcesz, aby powłoka analizowała łańcuch. Aby to zrobić, musisz przekazać swój wkład do powłoki w taki sposób, aby nawet nie próbowała go parsować. Jednym ze sposobów, aby to zrobić, jest read -r -p 'input: ' && printf '%q\n' "$REPLY"podanie danych wejściowych po wyświetleniu monitu.
Patrick,
1
Jak powiedziałem już kilka razy. Nie wiadomo, jak przekazać surowe dane do powłoki printf. Być może powinieneś zadać pytanie zamiast krytykować rozwiązanie, które nie ma nic wspólnego z twoim problemem.
Patrick,
4

Próbować:-

file=Dr\'\ A.tif
echo $file
Dr' A.tif

lub

file="Dr' A.tif"
echo $file
Dr' A.tif

lub jeśli ciąg zawiera podwójny cudzysłów: -

file='Dr" A.tif'
echo $file
Dr" A.tif

Istnieją dobre samouczki na temat ucieczki i cytowania w sieci. Zacznij od tego .

garethTheRed
źródło
1

W skrypcie nie trzeba uciekać żadnych nazw plików obsługiwanych przez użytkownika. Ucieczka jest konieczna tylko wtedy, gdy chcesz umieścić nazwę pliku jako literał w skrypcie lub przekazać kilka nazw plików jako pojedynczy strumień wejściowy do innego skryptu.

Skoro jesteś zapętlenie poprzez wyjście find, to jest jednym z najprostszych sposobów (!) Obsłużyć każdą możliwą ścieżkę :

while IFS= read -r -d ''
do
    file_namex="$(basename -- "$REPLY"; echo x)"
    file_name="${file_namex%$'\nx'}"
    do_something -- "$file_name"
done <(find "$some_path" -exec printf '%s\0' {} +)
l0b0
źródło
0

szybkie i (bardzo) brudne

find . | sed 's/^/"/' | sed 's/$/"/'
Michael
źródło
-1

Wiele z tych odpowiedzi, w tym najczęściej wybierane printf "%q", nie będzie działać we wszystkich przypadkach bez dodatkowej manipulacji. Sugerowałbym następujące (przykład poniżej):

cat <<EOF; 2015-11-07T03:34:41Z app[postgres.0000]: [TAG] text-search query doesn't contain lexemes: "" EOF

użytkownik3467349
źródło
Przepraszam za pytanie n00b, ale czy potrafisz wyjaśnić, w jaki sposób użyłbyś tego do unikania znaków w ciągu? Po skopiowaniu kodu, który piszesz do mojego terminala, po prostu drukuje 2015-11-07T03: 34: 41Z aplikacja [postgres.0000]: [TAG] zapytanie do wyszukiwania tekstu nie zawiera leksemów: „”… nic nie jest chronione .
SharkAlley
To dosłownie chodzi o to, że wszystkie znaki specjalne interpretowane jako znaki dosłowne, a nie ze względu na ich specjalne znaczenie. Zamiast tego spróbuj powtórzyć „powyższy ciąg”, aby zobaczyć różnicę.
user3467349,