Самый короткий способ удалить символы в переменной

Самый короткий способ удалить символы в переменной

Существует много способов удалить символы из переменной.

Самый короткий путь, который я узнал на данный момент, это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

Связанный контент