Comment échapper des caractères spéciaux dans une chaîne?

16

Supposons $fileque la valeur d'un nom de fichier soit conservée, par exemple Dr' A.tif. Dans la programmation bash, comment pourrais-je échapper aux guillemets simples et à tout autre caractère spécial du $filesans supprimer le caractère spécial?

Mise à jour du 9 juillet 2014

À la demande de @Gilles , extrait de code suivant qui ne peut pas gérer 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}

Après avoir essayé la réponse de @Patrick sur line 1, il semble fonctionner pour moi. Mais si j'ai un fichier tel que Dr\^s A.tif, la printfcommande ne semble pas aider, elle me le montre Dr\^s\ A.tif. Si je l'essaye manuellement sur la console comme ceci:

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

J'aurai cette sortie:

Dr\\\^s\ A.tif

Une idée de comment gérer ça?

huahsin68
la source
3
dans quel contexte prévoyez-vous d'utiliser $ file? ou ce problème affecte-t-il la chaîne avec le caractère spécial à $ file?
voler le
En fait, la commande find me renvoie un tableau de liste de fichiers. Et puis je boucle cette liste de fichiers dans la variable $ file.
huahsin68
2
On vous a donné des réponses correctes ici, mais ce ne sont probablement pas les réponses à la question que vous posez vraiment. D'après votre commentaire ici, je soupçonne fortement que vous êtes sur la mauvaise voie. Nous ne pouvons pas vous aider beaucoup car vous ne montrez pas votre code. Je vous recommande cependant de lire Pourquoi mon script shell s'étouffe-t-il sur les espaces ou autres caractères spéciaux? comme arrière-plan. Montrez votre script et expliquez ce que vous voulez faire.
Gilles 'SO- arrête d'être méchant'

Réponses:

14

Vous pouvez utiliser la fonction printfintégrée %qpour accomplir cela. Par exemple:

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

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

De la documentation bash sur 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
la source
Cela ne fonctionne pas avec certains cas .eg lorsque vous devez conserver les guillemets doubles.
user3467349
@ user3467349 cela fonctionne très bien avec des guillemets. Je parie que vous essayez quelque chose comme ça printf '%s' "foo". Vous devez d'abord comprendre le fonctionnement de l'analyse des arguments dans le shell. Voir gnu.org/software/bash/manual/bash.html#Shell-Operation # 2 se produit avant # 6.
Patrick
Pourriez-vous fournir un exemple de cette chaîne imprimée avec printfet sans autres manipulationsIt doesn't have a: ""
user3467349
Votre problème n'a rien à voir avec printf. Votre problème est que vous ne voulez pas que le shell analyse votre chaîne. Pour ce faire, vous devez transmettre votre entrée au shell de manière à ce qu'il n'essaye même pas de l'analyser. Une façon de le faire serait de read -r -p 'input: ' && printf '%q\n' "$REPLY"fournir l'entrée lorsque vous y êtes invité.
Patrick
1
Comme je l'ai dit plusieurs fois maintenant. Ne pas savoir comment transmettre des données brutes au shell n'est pas un problème printf. Vous devriez peut-être poser une question plutôt que de critiquer une solution qui n'a rien à voir avec votre problème.
Patrick
4

Essayer:-

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

ou

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

ou si la chaîne contient un guillemet double: -

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

Il y a de bons tutoriels sur l'évasion et les citations sur le net. Commencez avec celui-ci .

garethTheRed
la source
1

Vous n'avez pas besoin d'échapper aux noms de fichiers que vous gérez dans un script. L'échappement n'est nécessaire que si vous souhaitez mettre un nom de fichier en tant que littéral dans un script ou passer plusieurs noms de fichier en tant que flux d'entrée unique à un autre script.

Puisque vous êtes en boucle à travers la sortie find, c'est l' une des façons les plus simples à (!) Gérer tous les chemins possibles :

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
la source
0

rapide et (très) sale

find . | sed 's/^/"/' | sed 's/$/"/'
Michael
la source
-1

Un grand nombre de ces réponses, y compris celle la plus votée printf "%q", ne fonctionneront pas dans tous les cas sans manipulation supplémentaire. Je suggérerais ce qui suit (exemple ci-dessous):

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

user3467349
la source
Excusez la question n00b, mais pouvez-vous expliquer comment vous utiliseriez cela pour échapper des caractères dans une chaîne? Si je copie le code que vous écrivez dans mon terminal, il imprime simplement 2015-11-07T03: 34: 41Z app [postgres.0000]: [TAG] la requête de recherche de texte ne contient pas de lexèmes: "" ... rien n'est échappé .
SharkAlley
C'est littéralement le point, tous les caractères spéciaux sont interprétés comme des caractères littéraux et non pour leur signification spéciale. Essayez d' écho "la chaîne ci-dessus" à la place pour voir la différence.
user3467349