Welke tekens moeten worden geëscaped bij gebruik van Bash?

Is er een uitgebreide lijst met tekens die in Bash moeten worden ontsnapt? Kan het alleen worden gecontroleerd met sed?

In het bijzonder was ik aan het controleren of %moet worden ontsnapt of niet. Ik heb geprobeerd

echo "h%h" | sed 's/%/i/g'

en werkte prima, zonder te ontsnappen aan %. Betekent dit dat %niet hoeft te worden escaped? Was dit een goede manier om de noodzaak te controleren?

En meer algemeen: zijn het dezelfde tekens om te escapen in shellen bash?


Antwoord 1, autoriteit 100%

Er zijn twee eenvoudige en veilige regels die niet alleen werken in shmaar ook in bash.

1. Zet de hele string tussen enkele aanhalingstekens

Dit werkt voor alle tekens behalve het enkele aanhalingsteken zelf. Om aan het enkele aanhalingsteken te ontsnappen, sluit u het aanhalingsteken ervoor, voegt u het enkele aanhalingsteken toe en opent u het aanhalingsteken opnieuw.

'I'\''m a s@fe $tring which ends in newline
'

sed commando: sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"

2. Ontsnap aan elk teken met een backslash

Dit werkt voor alle karakters behalve newline. Gebruik voor nieuwe regeltekens enkele of dubbele aanhalingstekens. Lege strings moeten nog steeds worden verwerkt – vervang door ""

\I\'\m\ \a\ \s\@\f\e\ \$\t\r\i\n\g\ \w\h\i\c\h\ \e\n\d\s\ \i\n\ \n\e\w\l\i\n\e"
"

sed commando: sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.

2b. Beter leesbare versie van 2

Er is een eenvoudige veilige set tekens, zoals [a-zA-Z0-9,._+:@%/-], die zonder escapecodes kan worden gelaten om het leesbaarder te houden

I\'m\ a\ s@fe\ \$tring\ which\ ends\ in\ newline"
"

sed commando: LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.


Merk op dat men in een sed-programma niet kan weten of de laatste regel van invoer eindigt met een nieuwe regelbyte (behalve wanneer deze leeg is). Daarom gaan beide bovenstaande sed-commando’s ervan uit dat dit niet het geval is. U kunt een nieuwe regel met aanhalingstekens handmatig toevoegen.

Houd er rekening mee dat shell-variabelen alleen worden gedefinieerd voor tekst in de POSIX-zin. Het verwerken van binaire gegevens is niet gedefinieerd. Voor de implementaties die er toe doen, werkt binair met uitzondering van NUL-bytes (omdat variabelen worden geïmplementeerd met C-strings en bedoeld zijn om te worden gebruikt als C-strings, namelijk programmaargumenten), maar je moet overschakelen naar een “binaire” landinstelling zoals latin1 .


(Je kunt de regels gemakkelijk valideren door de POSIX-specificatie voor shte lezen. Raadpleeg voor bash de referentiehandleidinggekoppeld door @AustinPhillips)


Antwoord 2, autoriteit 22%

formaat dat kan worden hergebruikt als shell-invoer

Bewerk februari 2021: bash${var@Q}

Onder bash kunt u uw variabele inhoud opslaan met Parameter Expansion‘s @-opdracht voor Parametertransformatie:

${parameter@operator}
       Parameter transformation.  The expansion is either a transforma‐
       tion of the value of parameter or  information  about  parameter
       itself,  depending on the value of operator.  Each operator is a
       single letter:
       Q      The expansion is a string that is the value of  parameter
              quoted in a format that can be reused as input.
...
       A      The  expansion  is  a string in the form of an assignment
              statement or declare command  that,  if  evaluated,  will
              recreate parameter with its attributes and value.

Voorbeeld:

$ var=$'Hello\nGood world.\n'
$ echo "$var"
Hello
Good world.
$ echo "${var@Q}"
$'Hello\nGood world.\n'
$ echo "${var@A}"
var=$'Hello\nGood world.\n'

Oud antwoord

Er is een specialeprintfformaatrichtlijn (%q) gebouwd voor dit soort verzoeken:

printf [-v var] formaat [argumenten]

%q     causes printf to output the corresponding argument
        in a format that can be reused as shell input.

Enkele voorbeelden:

read foo
Hello world
printf "%q\n" "$foo"
Hello\ world
printf "%q\n" $'Hello world!\n'
$'Hello world!\n'

Dit kan ook via variabelen worden gebruikt:

printf -v var "%q" "$foo
"
echo "$var"
$'Hello world\n'

Snelle controle met alle (128) ascii-bytes:

Houd er rekening mee dat alle bytes van 128 tot 255 moeten worden geëscaped.

for i in {0..127} ;do
    printf -v var \\%o $i
    printf -v var $var
    printf -v res "%q" "$var"
    esc=E
    [ "$var" = "$res" ] && esc=-
    printf "%02X %s %-7s\n" $i $esc "$res"
done |
    column

Dit moet iets opleveren als:

00 E ''         1A E $'\032'    34 - 4          4E - N          68 - h      
01 E $'\001'    1B E $'\E'      35 - 5          4F - O          69 - i      
02 E $'\002'    1C E $'\034'    36 - 6          50 - P          6A - j      
03 E $'\003'    1D E $'\035'    37 - 7          51 - Q          6B - k      
04 E $'\004'    1E E $'\036'    38 - 8          52 - R          6C - l      
05 E $'\005'    1F E $'\037'    39 - 9          53 - S          6D - m      
06 E $'\006'    20 E \          3A - :          54 - T          6E - n      
07 E $'\a'      21 E \!         3B E \;         55 - U          6F - o      
08 E $'\b'      22 E \"         3C E \<         56 - V          70 - p      
09 E $'\t'      23 E \#         3D - =          57 - W          71 - q      
0A E $'\n'      24 E \$         3E E \>         58 - X          72 - r      
0B E $'\v'      25 - %          3F E \?         59 - Y          73 - s      
0C E $'\f'      26 E \&         40 - @          5A - Z          74 - t      
0D E $'\r'      27 E \'         41 - A          5B E \[         75 - u      
0E E $'\016'    28 E \(         42 - B          5C E \\         76 - v      
0F E $'\017'    29 E \)         43 - C          5D E \]         77 - w      
10 E $'\020'    2A E \*         44 - D          5E E \^         78 - x      
11 E $'\021'    2B - +          45 - E          5F - _          79 - y      
12 E $'\022'    2C E \,         46 - F          60 E \`         7A - z      
13 E $'\023'    2D - -          47 - G          61 - a          7B E \{     
14 E $'\024'    2E - .          48 - H          62 - b          7C E \|     
15 E $'\025'    2F - /          49 - I          63 - c          7D E \}     
16 E $'\026'    30 - 0          4A - J          64 - d          7E E \~     
17 E $'\027'    31 - 1          4B - K          65 - e          7F E $'\177'
18 E $'\030'    32 - 2          4C - L          66 - f      
19 E $'\031'    33 - 3          4D - M          67 - g      

Waar het eerste veld de hexa-waarde van de byte is, bevat het tweede veld Eals er een escape-teken nodig is en het derde veld toont de escape-presentatie van het teken.

Waarom ,?

Het kan zijn dat u enkele tekens ziet die niet altijdeen escapeteken behoeven, zoals ,, }en {.

Dus niet altijdmaar soms:

echo test 1, 2, 3 and 4,5.
test 1, 2, 3 and 4,5.

of

echo test { 1, 2, 3 }
test { 1, 2, 3 }

maar let op:

echo test{1,2,3}
test1 test2 test3
echo test\ {1,2,3}
test 1 test 2 test 3
echo test\ {\ 1,\ 2,\ 3\ }
test  1 test  2 test  3
echo test\ {\ 1\,\ 2,\ 3\ }
test  1, 2 test  3 

Antwoord 3, autoriteit 14%

Om iemand anders te behoeden voor RTFM… in bash:

Door tekens tussen dubbele aanhalingstekens te plaatsen, blijft de letterlijke waarde van alle tekens binnen de aanhalingstekens behouden, met uitzondering van $, `, \, en, wanneer geschiedenisuitbreiding is ingeschakeld, !.

…dus als je daaraan ontsnapt (en het citaat zelf natuurlijk), zit je waarschijnlijk goed.

Als je een meer conservatieve benadering kiest van ‘bij twijfel, ontsnap eraan’, zou het mogelijk moeten zijn om in plaats daarvan tekens met een speciale betekenis te vermijden door identifier-tekens (d.w.z. ASCII-letters, cijfers of ‘_’) niet te laten ontsnappen. Het is zeer onwaarschijnlijk dat deze ooit (d.w.z. in een of andere rare POSIX-achtige shell) een speciale betekenis hebben en dus moeten worden ontsnapt.


Antwoord 4, autoriteit 10%

Met behulp van de print '%q'techniekkunnen we een lus uitvoeren om uit welke tekens speciaal zijn:

#!/bin/bash
special=$'`!@#$%^&*()-_+={}|[]\\;\':",.<>?/ '
for ((i=0; i < ${#special}; i++)); do
    char="${special:i:1}"
    printf -v q_char '%q' "$char"
    if [[ "$char" != "$q_char" ]]; then
        printf 'Yes - character %s needs to be escaped\n' "$char"
    else
        printf 'No - character %s does not need to be escaped\n' "$char"
    fi
done | sort

Het geeft deze output:

No, character % does not need to be escaped
No, character + does not need to be escaped
No, character - does not need to be escaped
No, character . does not need to be escaped
No, character / does not need to be escaped
No, character : does not need to be escaped
No, character = does not need to be escaped
No, character @ does not need to be escaped
No, character _ does not need to be escaped
Yes, character   needs to be escaped
Yes, character ! needs to be escaped
Yes, character " needs to be escaped
Yes, character # needs to be escaped
Yes, character $ needs to be escaped
Yes, character & needs to be escaped
Yes, character ' needs to be escaped
Yes, character ( needs to be escaped
Yes, character ) needs to be escaped
Yes, character * needs to be escaped
Yes, character , needs to be escaped
Yes, character ; needs to be escaped
Yes, character < needs to be escaped
Yes, character > needs to be escaped
Yes, character ? needs to be escaped
Yes, character [ needs to be escaped
Yes, character \ needs to be escaped
Yes, character ] needs to be escaped
Yes, character ^ needs to be escaped
Yes, character ` needs to be escaped
Yes, character { needs to be escaped
Yes, character | needs to be escaped
Yes, character } needs to be escaped

Sommige resultaten, zoals ,zien er een beetje verdacht uit. Zou interessant zijn om de input van @CharlesDuffy hierover te krijgen.


Antwoord 5, autoriteit 6%

Tekens die moeten worden ontsnapt, zijn in Bourne- of POSIX-shell anders dan in Bash. Over het algemeen is (zeer) Bash een superset van die shells, dus alles wat u in shellescapet, moet in Bash worden geëscaped.

Een mooie algemene regel zou zijn: “bij twijfel, ontsnap eraan”. Maar door sommige tekens te escapen, krijgen ze een speciale betekenis, zoals \n. Deze staan vermeld op de man bash-pagina’s onder Quotingen echo.

Behalve, ontsnap aan elk teken dat niet alfanumeriek is, het is veiliger. Ik ken geen enkele definitieve lijst.

De man-pagina’s vermelden ze allemaal ergens, maar niet op één plek. Leer de taal, dat is de manier om zeker te zijn.

Eentje die me heeft betrapt is !. Dit is een speciaal teken (geschiedenisuitbreiding) in Bash (en csh) maar niet in Korn-shell. Zelfs echo "Hello world!"geeft problemen. Het gebruik van enkele aanhalingstekens, zoals gewoonlijk, verwijdert de speciale betekenis.


Antwoord 6, autoriteit 2%

Ik neem aan dat je het hebt over bash-snaren. Er zijn verschillende soorten snaren die een andere reeks vereisten hebben om te ontsnappen. bijv. Single Quotes-snaren zijn anders dan dubbele geciteerde snaren.

De beste referentie is de citeren sectie van de bash handleiding.

Het legt uit welke personages te ontsnappen. Merk op dat sommige tekens mogelijk ontsnappen, afhankelijk van welke opties zijn ingeschakeld, zoals de uitbreiding van de geschiedenis.


Antwoord 7, Autoriteit 2%

Ik heb gemerkt dat Bash automatisch aan sommige tekens ontsnapt bij gebruik van automatisch compleet.

Als u bijvoorbeeld een map met de naam dir:Ahebt, wordt BASH automatisch ingedrukt voor dir\:A

Gebruik dit, ik heb enkele experimenten uitgevoerd met behulp van tekens van de ASCII-tabel en afgeleid van de volgende lijsten:

tekens die bashen ontsnapt op automatisch voltooien : (inclusief ruimte)

!"$&'()*,:;<=>?@[\]^`{|}

tekens die bash niet ontsnapt :

#%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~

(Ik heb /uitgesloten, omdat deze niet in de directory-namen kan worden gebruikt)

Other episodes