Существует много способов удалить символы из переменной.
Самый короткий путь, который я узнал на данный момент, этоtr
:
OUTPUT=a\'b\"c\`d_123and_a_lot_more
OUTPUT=$(echo "$OUTPUT"|tr -d "'\`\"")
echo $OUTPUT
Есть ли более быстрый способ?
И безопасно ли это цитирование для таких кавычек, как '
, "
и `
самого себя?
решение1
Давайте посмотрим. Самое короткое, что я могу придумать, это модификация вашего tr
решения:
OUTPUT="$(tr -d "\"\`'" <<<$OUTPUT)"
Другие альтернативы включают уже упомянутую замену переменных, которая может быть короче, чем показано до сих пор:
OUTPUT="${OUTPUT//[\'\"\`]}"
И, sed
конечно, хотя это и длиннее по количеству символов:
OUTPUT="$(sed s/[\'\"\`]//g <<<$OUTPUT)"
Я не уверен, имеете ли вы в виду кратчайший по длине или по времени. С точки зрения длины эти два варианта самые короткие (или насколько я могу получить) для удаления этих конкретных символов. Итак, что быстрее? Я проверил, установив переменную OUTPUT
на то, что было в вашем примере, но повторив несколько десятков раз:
$ echo ${#OUTPUT}
4900
$ time tr -d "\"\`'" <<<$OUTPUT
real 0m0.002s
user 0m0.004s
sys 0m0.000s
$ time sed s/[\'\"\`]//g <<<$OUTPUT
real 0m0.005s
user 0m0.000s
sys 0m0.000s
$ time echo ${OUTPUT//[\'\"\`]}
real 0m0.027s
user 0m0.028s
sys 0m0.000s
Как вы можете видеть, tr
является явно самым быстрым, за ним следует sed
. Кроме того, кажется, что использование echo
на самом деле немного быстрее, чем использование <<<
:
$ for i in {1..10}; do
( time echo $OUTPUT | tr -d "\"\`'" > /dev/null ) 2>&1
done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}';
0.0025
$ for i in {1..10}; do
( time tr -d "\"\`'" <<<$OUTPUT > /dev/null ) 2>&1
done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}';
0.0029
Поскольку разница незначительна, я провел вышеуказанные тесты по 10 раз для каждого из двух тестов, и оказалось, что самым быстрым действительно оказался тот, с которого вы начинали:
echo $OUTPUT | tr -d "\"\`'"
Однако ситуация меняется, если принять во внимание накладные расходы на присвоение переменной. В данном случае использование tr
немного медленнее, чем простая замена:
$ for i in {1..10}; do
( time OUTPUT=${OUTPUT//[\'\"\`]} ) 2>&1
done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}';
0.0032
$ for i in {1..10}; do
( time OUTPUT=$(echo $OUTPUT | tr -d "\"\`'")) 2>&1
done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}';
0.0044
Итак, в заключение, когда вы просто хотите просмотреть результаты, используйте , tr
но если вы хотите переназначить переменную, использование функций обработки строк оболочки будет быстрее, поскольку они позволяют избежать накладных расходов на запуск отдельной подоболочки.
решение2
Вы могли бы использоватьзамена переменной:
$ OUTPUT=a\'b\"c\`d
$ echo "$OUTPUT"
a'b"c`d
Используйте этот синтаксис, ${parameter//pattern/string}
чтобы заменить все вхождения шаблона строкой.
$ echo "${OUTPUT//\'/x}"
axb"c`d
$ echo "${OUTPUT//\"/x}"
a'bxc`d
$ echo "${OUTPUT//\`/x}"
a'b"cxd
$ echo "${OUTPUT//[\'\"\`]/x}"
axbxcxd
решение3
В bash или zsh это:
OUTPUT="${OUTPUT//[\`\"\']/}"
Обратите внимание, что ${VAR//PATTERN/}
удаляет все экземпляры шаблона. Для получения дополнительной информациирасширение параметра bash
Это решение должно быть самым быстрым для коротких строк, поскольку не требует запуска внешних программ. Однако для очень длинных строк верно обратное — лучше использовать специальный инструмент для текстовых операций, например:
$ OUTPUT="$(cat /usr/src/linux/.config)"
$ time (echo $OUTPUT | OUTPUT="${OUTPUT//set/abc}")
real 0m1.766s
user 0m1.681s
sys 0m0.002s
$ time (echo $OUTPUT | sed s/set/abc/g >/dev/null)
real 0m0.094s
user 0m0.078s
sys 0m0.006s
решение4
Если, на всякий случай, вы просто пытаетесь обработать кавычки для повторного использования в оболочке, то вы можете сделать этобезудалить их, и это тоже очень просто:
aq() { sh -c 'for a do
alias "$((i=$i+1))=$a"
done; alias' -- "$@"
}
Эта функция shell заключает в кавычки любой переданный ей массив аргументов и увеличивает его вывод на каждый итерируемый аргумент.
Вот он с несколькими аргументами:
aq \
"here's an
ugly one" \
"this one is \$PATHpretty bad, too" \
'this one```****```; totally sucks'
ВЫХОД
1='here'"'"'s an
ugly one'
2='this one is $PATHpretty bad, too'
3='this one```****```; totally sucks'
Этот вывод — то, из чего обычно делаются dash
безопасные одинарные кавычки, например '"'"'
.bash
'\''
Замену выбранного набора одиночных, непустых и ненулевых байтов другим одиночным байтом, скорее всего, можно выполнить быстрее всего в любой оболочке POSIX с помощью $IFS
и $*
.
set -f; IFS=\"\'\`; set -- $var; printf %s "$*"
ВЫХОД
"some ""crazy """"""""string ""here
Я просто printf
это сделал, чтобы вы могли это увидеть, но, конечно, если бы я это сделал:
var="$*"
...а не значением printf
команды $var
будет то, что вы видите в выводе.
Когда я set -f
даю команду оболочкенетдля glob - в случае, если строка содержит символы, которые могут быть истолкованы как шаблоны glob. Я делаю это, потому что парсер оболочки расширяет шаблоны globпослеон выполняет разделение полей на переменные. глобализацию можно включить снова, как set +f
. В общем - в скриптах - я считаю полезным устанавливать свой bang следующим образом:
#!/usr/bin/sh -f
А затем кявно включить подстановкус set +f
любой линией, которая мне нужна.
Разделение полей происходит на основе символов в $IFS
.
Существует два типа $IFS
значений — $IFS
пробельные и $IFS
непробельные $IFS
.(пробел, табуляция, новая строка)Поля с разделителями указаны для исключенияпоследовательностьв одно поле(или вообще ни одного, если они не предшествуют чему-либо другому)- так...
IFS=\ ; var=' '; printf '<%s>' $var
<>
Но все остальные указаны для оценки в одном поле.за каждое событие- они не усечены.
IFS=/; var='/////'; printf '<%s>' $var
<><><><><>
ВсеРасширения переменных по умолчанию представляют собой $IFS
массивы данных с разделителями — они разделяются на отдельные поля в соответствии с $IFS
. Когда вы "
заключаете один в кавычки, вы переопределяете это свойство массива и оцениваете его как одну строку.
Поэтому, когда я это сделаю...
IFS=\"\'\`; set -- $var
Я устанавливаю массив аргументов оболочки на множество $IFS
разделенных полей, сгенерированных $var
расширением . Когда он расширяется, его составляющие значения для символов, содержащихся в , $IFS
являютсяпотерянный- теперь они всего лишь разделители полей - они есть \0NUL
.
"$*"
- как и другие расширения переменных в двойных кавычках - также переопределяет свойства разделения полей $IFS
. Но,кроме того, он заменяет первый байт в$IFS
для каждого разделенного поляв "$@"
. Так потому что "
былпервыйзначение в$IFS
все последующие разделители "
становятся "$*"
.И "
не обязательно, $IFS
когда вы его делите. Вы можете изменить$IFS
после set -- $args
к совершенно другому значению и егоновыйТогда первый байт будет отображаться для разделителей полей в "$*"
. Более того, вы можете полностью удалить все их следы следующим образом:
set -- $var; IFS=; printf %s "$*"
ВЫХОД
some crazy string here