Skrypt powłoki - usuń pierwszy i ostatni cudzysłów („) ze zmiennej

225

Poniżej znajduje się fragment skryptu powłoki z większego skryptu. Usuwa cudzysłowy z łańcucha przechowywanego przez zmienną. Robię to za pomocą sed, ale czy jest wydajne? Jeśli nie, to jaki jest skuteczny sposób?

#!/bin/sh

opt="\"html\\test\\\""
temp=`echo $opt | sed 's/.\(.*\)/\1/' | sed 's/\(.*\)./\1/'`
echo $temp
użytkownik1263746
źródło
Sugerowałbym użycie sed "s/^\(\"\)\(.*\)\1\$/\2/g" <<<"$opt". Ta składnia usunie qoutes tylko wtedy, gdy istnieje pasująca para.
John Smith,
@JohnSmith Muszę również automatycznie unikać cudzysłowów w skrypcie powłoki, ale muszę to zrobić niezależnie od tego, czy są one zgodne, czy nie, więc prawdopodobnie nie użyję tego wyrażenia, które opublikowałeś.
Pysis
Jeśli znalazłeś to pytanie i chcesz po prostu usunąć wszystkie cytaty, zapoznaj się z odpowiedzią: askubuntu.com/a/979964/103498 .
Gordon Bean

Odpowiedzi:

268

Jest prostszy i bardziej wydajny sposób, używając funkcji usuwania prefiksu / sufiksu natywnej powłoki:

temp="${opt%\"}"
temp="${temp#\"}"
echo "$temp"

${opt%\"}usunie sufiks "(znak ucieczki z ukośnikiem odwrotnym, aby zapobiec interpretacji powłoki).

${temp#\"}usunie prefiks "(znak ucieczki z ukośnikiem odwrotnym, aby zapobiec interpretacji powłoki).

Kolejną zaletą jest to, że usunie otaczające cytaty tylko wtedy, gdy istnieją otaczające cytaty.

BTW, twoje rozwiązanie zawsze usuwa pierwszy i ostatni znak, bez względu na to, jakie mogą być (oczywiście, jestem pewien, że znasz swoje dane, ale zawsze lepiej być pewnym, co usuwasz).

Za pomocą sed:

echo "$opt" | sed -e 's/^"//' -e 's/"$//'

(Poprawiona wersja, jak wskazuje jfgagne, pozbycie się echa)

sed -e 's/^"//' -e 's/"$//' <<<"$opt"

Zastępuje zatem prowadzenie "bez niczego, a ciągnięcie "z niczym. W tej samej inwokacji (nie trzeba instalować potoków i uruchamiać kolejnej wersji. Za pomocą -emożna przetwarzać wiele tekstów).

huelbois
źródło
14
Możesz pozbyć się rury w roztworze sed za pomocą sed -e 's/^"//' -e 's/"$//' <<< $opt.
jfg956
1
Może to źle rozumieć, że ciąg nie ma zarówno wiodącego, jak i końcowego cudzysłowu. Jakieś sugestie, jak sobie z tym poradzić?
jesears
Aby poradzić sobie tylko z dopasowanymi cudzysłowami, zobacz odpowiedź: @Joachim Pileborg
jsears,
1
@jsears Huh? To konkretnie przycina jeden od początku, jeśli taki istnieje, i jeden od końca, jeśli taki istnieje. Jeśli nie chcesz usuwać, chyba że oba są obecne; tak, można użyć jednego sedwyrażenia regularnego lub podstawienie zmiennej może być zawinięte w coś w wierszucase $opt in '"'*'"') ... do it ... ;; esac
tripleee
2
Można uniknąć wielu wyrażeń, używającsed 's/^"\|"$//g'
tzrlk
287

Użyj tr, aby usunąć ":

 echo "$opt" | tr -d '"'

Uwaga : Usuwa to wszystkie podwójne cudzysłowy, nie tylko początkowe i końcowe.

wieczorek1990
źródło
16
Nie o to prosił OP, ponieważ usuwa również wszystkie cytaty, nie tylko te wiodące i końcowe.
Lenar Hoyt,
19
Chociaż nie odpowiada wprost na OP, rozwiązuje to dla mnie, ponieważ na początku i na końcu „/ path / file-name” występują tylko podwójne cudzysłowy. Brak osadzonych podwójnych cudzysłowów. +1
WinEunuuchs2Unix
6
To rozwiązanie będzie tym, czego chce wiele osób. W wielu przypadkach skrypty mogą łatwo sprowadzić wykonalne dane do ciągu otoczonego cudzysłowami.
Shadoninja
2
Elegancki i uratował mnie przed znacznie dłuższym czasem debugowania. Dzięki.
Scott Wade,
Właśnie tego chce się najlepiej i dlatego ma więcej głosów niż zaakceptowana odpowiedź.
apurvc
38

Spowoduje to usunięcie wszystkich podwójnych cudzysłowów.

echo "${opt//\"}"
Steven Penny
źródło
1
Ale zerwie z uciekającymi cytatami, np Don't remove this \" quote. :-(
gniourf_gniourf
Jest to rozszerzenie Bash, więc ogólnie nie dotyczy skryptu powłoki. (Pytanie jest dwuznaczne, czy jest to ważne, czy nie. Użytkownicy, którzy znajdą to w Google, mogą szukać rozwiązania POSIX).
tripleee
Doskonałość! Dokładnie to, czego potrzebowałem. Uwielbiam takie pytania, które dają mnóstwo odpowiedzi, a nie tylko to, czego zażądał OP. Klejnoty znajdują się w niedopuszczalnych odpowiedziach!
dlite922
Obawiam się, że OP chce czegoś, co usuwa tylko wiodące / końcowe cudzysłowy i zachowuje te, które uciekły.
Fernando Paz
36

Istnieje prosty sposób użycia xargs :

> echo '"quoted"' | xargs
quoted

xargs używa echa jako polecenia domyślnego, jeśli nie podano polecenia, i usuwa cudzysłowy z wejścia. Zobacz np . Tutaj .

użytkownik1587520
źródło
echo '"quoted"' | xargs -> quoted
kyb
1
Działa to, ale tylko dla parzystej liczby cytatów w ciągu. Jeśli liczba nieparzysta, generuje błąd.
James Shewey,
21

Możesz to zrobić za pomocą tylko jednego połączenia, aby sed:

$ echo "\"html\\test\\\"" | sed 's/^"\(.*\)"$/\1/'
html\test\
Jakiś programista, koleś
źródło
19

Najkrótsza droga - spróbuj:

echo $opt | sed "s/\"//g"

W rzeczywistości usuwa wszystkie "s (podwójne cudzysłowy) z opt(czy naprawdę będzie więcej podwójnych cudzysłowów innych niż na początku i na końcu? Więc to w rzeczywistości to samo i znacznie krótsze ;-))

Dr.Kameleon
źródło
4
Jeśli chcesz usunąć wszystkie podwójne cudzysłowy, jeszcze lepiej jest użyć: „$ {opt // \" /} ". Bez potoku, bez podpowłoki ... I strzeż się, że jeśli masz spacje w opcjach, możesz stracić zawsze
cytuj
Cóż, zmienna w zasadzie odczytuje ścieżkę DocumentRoot z pliku konfiguracyjnego httpd, więc nie sądzę, aby mógł istnieć przypadek, w którym znak cudzysłowu mógłby znajdować się w ciągu. A ten sed jest o wiele ładniejszy i skuteczniejszy .... Dzięki!
user1263746,
16

Jeśli używasz jq i próbujesz usunąć cytaty z wyniku, inne odpowiedzi będą działać, ale jest lepszy sposób. Korzystając z -ropcji, możesz wydrukować wynik bez cudzysłowów.

$ echo '{"foo": "bar"}' | jq '.foo'
"bar"

$ echo '{"foo": "bar"}' | jq -r '.foo'
bar
człowiek psujący zabawę
źródło
Używam, jqale pamiętaj, że to nie działa, jeśli jq wypisuje również wyjście ASCII za pomocą -a(jest to błąd po stronie jq)
Can Poyrazoğlu
yqma również -ropcję:yq -r '.foo' file.yml
Hlib Babii
12

Aktualizacja

Prosta i elegancka odpowiedź z usuwania pojedynczych i podwójnych cudzysłowów w ciągu za pomocą tylko poleceń bash / standardowych Linuksa :

BAR=$(eval echo $BAR) wycina cytaty z BAR .

================================================== ===========

Na podstawie odpowiedzi hueybois wymyśliłem tę funkcję po wielu próbach i błędach:

function stripStartAndEndQuotes {
    cmd="temp=\${$1%\\\"}"
    eval echo $cmd
    temp="${temp#\"}"
    eval echo "$1=$temp"
}

Jeśli nie chcesz niczego drukować, możesz przesłać ewaluacje /dev/null 2>&1 .

Stosowanie:

$ BAR="FOO BAR"
$ echo BAR
"FOO BAR"
$ stripStartAndEndQuotes "BAR"
$ echo BAR
FOO BAR
Jonathan Lin
źródło
1
Chociaż naprawdę podoba mi się ten prosty i elegancki, myślę, że warto zauważyć, że eval nie wykonuje tylko podwójnych cytatów, ale w rzeczywistości ocenia jako linię kodu. Dlatego ta technika może dawać nieoczekiwane wyniki, gdy BAR zawiera inne sekwencje, które mogą być zastąpione przez powłokę (np. BAR = \ 'abc \' lub BAR = ab \ 'c lub BAR = a \ $ b, lub BAR = a \ $ (ls *))
MaxP
11

Najłatwiejsze rozwiązanie w Bash:

$ s='"abc"'
$ echo $s
"abc"
$ echo "${s:1:-1}"
abc

Nazywa się to rozszerzaniem podciągów (zobacz Podręcznik Gnu Bash i wyszukaj ${parameter:offset:length}). W tym przykładzie bierze podłańcuch od spoczątku w pozycji 1 i kończąc na drugiej ostatniej pozycji. Wynika to z faktu, że jeśli lengthjest wartością ujemną, jest interpretowana jako przesunięcie wstecz w stosunku do końca parameter.

Hiro.Protagonist
źródło
ujemne długości obsługiwane przez bash
v4.2
8

Jest to najbardziej dyskretny sposób bez użycia sed:

x='"fish"'
printf "   quotes: %s\nno quotes:  %s\n" "$x" "${x//\"/}"

Lub

echo $x
echo ${x//\"/}

Wynik:

   quotes: "fish"
no quotes:  fish

Mam to ze źródła .

nimig18
źródło
Spowoduje to usunięcie cudzysłowów z ciągu oraz otaczających go ciągów.
jesears
Oprócz komentarza jest to identyczna z odpowiedzią @ StevenPenny z 2012 roku .
tripleee
3

Jest na to inny sposób. Lubić:

echo ${opt:1:-1}
DevilTour
źródło
2

Moja wersja

strip_quotes() {
    while [[ $# -gt 0 ]]; do
        local value=${!1}
        local len=${#value}
        [[ ${value:0:1} == \" && ${value:$len-1:1} == \" ]] && declare -g $1="${value:1:$len-2}"
        shift
    done
}

Funkcja akceptuje nazwy zmiennych i usuwa cudzysłowy. Usuwa tylko pasującą parę wiodących i końcowych cytatów. Nie sprawdza, czy cytat końcowy jest zastępowany (poprzedzony przez\ tym, że sam nie jest zastępowany).

Z mojego doświadczenia wynika, że ​​funkcje ogólnego zastosowania ciągów znaków, takie jak ta (mam ich bibliotekę), są najbardziej wydajne podczas bezpośredniego manipulowania ciągami, nie używając żadnego dopasowania wzorca, a zwłaszcza nie tworząc żadnych podpowłok lub wywołując jakiekolwiek narzędzia zewnętrzne, takie jak sed, awklub grep.

var1="\"test \\ \" end \""
var2=test
var3=\"test
var4=test\"
echo before:
for i in var{1,2,3,4}; do
    echo $i="${!i}"
done
strip_quotes var{1,2,3,4}
echo
echo after:
for i in var{1,2,3,4}; do
    echo $i="${!i}"
done
Gene Pawłowski
źródło
0

Wiem, że to bardzo stare pytanie, ale tutaj jest kolejna odmiana sed, która może być przydatna dla kogoś. W przeciwieństwie do niektórych innych, zastępuje tylko podwójne cudzysłowy na początku lub na końcu ...

echo "$opt" | sed -r 's/^"|"$//g'
użytkownik1751825
źródło