Removendo aspas simples e duplas em uma string usando apenas comandos bash/padrão do Linux
Pergunta
Estou procurando algo que traduza uma string da seguinte maneira, usando apenas comandos bash/padrão do Linux:
- As aspas simples em torno de uma string devem ser removidas
- As aspas duplas em torno de uma string devem ser removidas
- Strings sem aspas devem permanecer as mesmas
- Strings com aspas vizinhas incomparáveis devem permanecer as mesmas
- As aspas simples que não circundam a string devem permanecer
- As aspas duplas que não circundam a string devem permanecer
Por exemplo:
- 'Alimento' deveria se tornar Alimento
- “Comida” deveria se tornar Comida
- A alimentação deve permanecer a mesma
- 'Comida" deve permanecer a mesma
- 'Comida' deve permanecer a mesma
- 'Fo'od' deveria se tornar Fo'od
- "Fo'od" deveria se tornar Fo'od
- Fo'od deve permanecer o mesmo
- 'Fo"od' deveria se tornar Fo"od
- "Fo"od" deveria se tornar Fo"od
- Fo"od deve permanecer o mesmo
Obrigado!
Solução
Isso deve servir:
sed "s/^\([\"']\)\(.*\)\1\$/\2/g" in.txt
Onde in.txt está:
"Fo'od'
'Food'
"Food"
"Fo"od'
Food
'Food"
"Food'
'Fo'od'
"Fo'od"
Fo'od
'Fo"od'
"Fo"od"
Fo"od
E esperado.txt é:
"Fo'od'
Food
Food
"Fo"od'
Food
'Food"
"Food'
Fo'od
Fo'od
Fo'od
Fo"od
Fo"od
Fo"od
Você pode verificar se eles correspondem a:
diff -s <(sed "s/^\([\"']\)\(.*\)\1\$/\2/g" in.txt) expected.txt
Outras dicas
Você poderia usar tr
:
echo "$string" | tr -d 'chars to delete'
...também funciona, no entanto, 'tr' é conhecido por ser problemático em distribuições muito mais antigas (por volta do Redhat 9-ish). tr
é uma abreviatura de 'translate', comumente usada em pipes para transformar a entrada.O -d
opção significa simplesmente 'excluir'.
A maioria das versões modernas também contém macros predefinidas para transformar superior em inferior, inferior em superior, eliminar espaços em branco, etc.Portanto, se você usá-lo, reserve um segundo para ver o que mais ele faz (consulte a saída de ajuda/página de manual), que é útil.
VAR="'FOOD'"
VAR=$(eval echo $VAR)
Explicação:Como as aspas já são compreendidas pelo shell, você pode pedir ao shell para avaliar um comando que apenas ecoa a string citada, da mesma forma que faz quando você mesmo digita.
Aqui, eval echo $VAR
expande para eval echo 'FOOD'
porque as cotações são na verdade parte do valor de VAR
.Se você fosse correr echo 'FOOD'
na casca que você obteria FOOD
(sem as aspas).Isso é o que eval
faz:ele pega sua entrada e a executa como um comando shell.
⚠INJEÇÃO DE CÓDIGO!
eval
expor scripts à injeção de código.VAR=';ls -l' VAR=$(eval echo $VAR)
causará a execução de
ls -l
.Códigos muito mais prejudiciais poderiam ser injetados aqui.
Você provavelmente deseja usar sed...
echo $mystring | sed -s "s/^\(\(\"\(.*\)\"\)\|\('\(.*\)'\)\)\$/\\3\\5/g"
Apenas usando recursos internos do Bash (ou seja,Expansão do parâmetro Bash):
IFS=' '
food_strings=( "'Food'" '"Food"' Food "'Food\"" "\"Food'" "'Fo'od'" "\"Fo'od\"" "Fo'od" "'Fo\"od'" '"Fo"od"' 'Fo"od' )
for food in ${food_strings[@]}; do
[[ "${food#\'}" != "$food" ]] && [[ "${food%\'}" != "$food" ]] && { food="${food#\'}"; food="${food%\'}"; }
[[ "${food#\"}" != "$food" ]] && [[ "${food%\"}" != "$food" ]] && { food="${food#\"}"; food="${food%\"}"; }
echo "$food"
done
Para mais um exemplo de expansão de parâmetros Bash, consulte:
Apenas tropecei nisso também.Para os três primeiros casos de teste, eval echo $string
funciona bem.Para fazê-lo funcionar em todos os casos solicitados e em alguns outros, criei isso (testado com bash
e dash
):
#!/bin/sh
stripquotes() {
local firstchar="`substr "$1" 0 1`"
local len=${#1}
local ilast=$((${#1} - 1))
local lastchar="`substr "$1" $(($len - 1))`"
if [ "$firstchar" = '"' ] || [ "$firstchar" = "'" ] && [ $firstchar = $lastchar ]; then
echo "`substr "$1" 1 $(($len - 2))`"
else
echo "$1"
fi
}
# $1 = String.
# $2 = Start index.
# $3 = Length (optional). If unspecified or an empty string, the length of the
# rest of the string is used.
substr() {
local "len=$3"
[ "$len" = '' ] && len=${#1}
if ! (echo ${1:$2:$len}) 2>/dev/null; then
echo "$1" | awk "{ print(substr(\$0, $(($2 + 1)), $len)) }"
fi
}
var="'Food'"
stripquotes "$var"
var='"Food"'
stripquotes "$var"
var=Food
stripquotes "$var"
var=\'Food\"
stripquotes "$var"
var=\"Food\'
stripquotes "$var"
var="'Fo'od'"
stripquotes "$var"
var="\"Fo'od\""
stripquotes "$var"
var="Fo'od"
stripquotes "$var"
var="'Fo\"od'"
stripquotes "$var"
var="\"Fo\"od\""
stripquotes "$var"
var="Fo\"od"
stripquotes "$var"
# A string with whitespace should work too.
var="'F\"o 'o o o' o\"d'"
stripquotes "$var"
# Strings that start and end with the same character that isn't a quote or
# doublequote should stay the same.
var="TEST"
stripquotes "$var"
# An empty string should not cause errors.
var=
stripquotes "$var"
# Strings of length 2 that begin and end with a quote or doublequote should not
# cause errors.
var="''"
stripquotes "$var"
var='""'
stripquotes "$var"
python -c "import sys;a=sys.stdin.read();a=a.strip();print (a[1:-1] if a[0]==a[-1] and a[0] in \"'\\\"\" else a)"
ele não lida muito bem com casos extremos (como uma string vazia), mas servirá como ponto de partida.Funciona distribuindo os caracteres da frente e de trás se eles forem iguais e se forem ' ou "