Исключение прямого и обратного слеша с помощью sed

Вот сценарий, который генерирует сценарий , который пытается перехватить все сигналы. Сценарий, который он генерирует ( sigcatcher.sh ), выводит имя пойманного сигнала перед завершением.

#!/bin/sh

# generate a shell function for each and every available signal.

/bin/kill -l | tr ' ' '\n' |
while read signal; do
    cat <<END_OF_FUNCTION
handle_$signal () {
  echo "Caught $signal"
  exit
}

trap 'handle_$signal' $signal

END_OF_FUNCTION
done >sigcatcher.sh

echo 'echo "$$"; sleep 600' >>sigcatcher.sh

В моей системе (OpenBSD) / bin / kill -l генерирует список доступных сигналов в одной строке, поэтому tr используется, чтобы разбить его.

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

handle_HUP () {
  echo "Caught HUP"
  exit
}

trap 'handle_HUP' HUP

handle_INT () {
  echo "Caught INT"
  exit
}

trap 'handle_INT' INT

(и т. Д.)

И он будет завершен с помощью

echo "$$"; sleep 600

Он выводит свой PID и находится в спящем режиме в течение 10 минут.

Вы запускаете его так:

$ sh ./sigcatcher.sh >sigcatcher.out

Затем вы закрываете окно и проверяете sigcatcher.out .

Я не запускаю X Windows, но когда я закрываю панель tmux , в которой выполняется этот сценарий, я получаю сообщение «Caught HUP» в выходном файле.

8
19.07.2017, 19:07
3 ответа

Вам нужно экранировать (обратной косой чертой\)все замененные косые черты /и все обратные косые черты \отдельно, поэтому:

$ echo "/tmp/test/folder1/test.txt" | sed 's/\//\\\//g'
\/tmp\/test\/folder1\/test.txt

но это довольно нечитабельно.

Однако sedпозволяет использовать в качестве разделителя практически любой символ вместо /, это особенно полезно, когда нужно заменить косую черту /, как в вашем случае, например, используя точку с запятой ;в качестве разделителя команда стала бы проще:

echo "/tmp/test/folder1/test.txt" | sed 's;/;\\/;g'

Другие случаи:

  • Если кто-то хочет придерживаться косой черты в качестве разделителя и использовать двойные кавычки, тогда все экранированные обратные косые черты должны быть экранированы еще раз, чтобы сохранить их буквальные значения:

    echo "/tmp/test/folder1/test.txt" | sed "s/\//\\\\\//g"
    
  • если вообще не нужны кавычки, то нужна еще одна обратная косая черта:

    echo "/tmp/test/folder1/test.txt" | sed s/\\//\\\\\\//g
    
42
27.01.2020, 20:08

Или, если значение находится в переменной оболочки (bash ):

var=/tmp/test/folder1/test.txt
$ echo "${var//\//\\\/}"
\/tmp\/test\/folder1\/test.txt

Первый //запуск параметра расширения , говорящий о замене всех совпадений с заменой. Следующий \/— это экранированный /для сопоставления, а \\\/— это экранированный \, за которым следует экранированный /в качестве замены.

7
27.01.2020, 20:08

Окончательное решение будет таким:

$ sed 's:/:\\/:g'  <<<"$str"
\/tmp\/test\/folder1\/test.txt

Но объяснить, как туда добраться:
Да, вы пропустили одну обратную косую черту:

$ str='/tmp/test/folder1/test.txt'
$ sed "s/\//\\\\\//g" <<<"$str"
\/tmp\/test\/folder1\/test.txt

Я надеюсь, что один пробел прояснит ситуацию:

$ sed "s/\//\\\\ \//g"  <<<"$str"
\ /tmp\ /test\ /folder1\ /test.txt

Но если вы измените разделитель sed на :(, например):

$  sed "s:\/:\\\\\/:g"  <<<"$str"
\/tmp\/test\/folder1\/test.txt

Но это не совсем правильно, так как (теперь не специальный)/не нуждается в скейпинге:

$ sed "s:/:\\\\/:g"  <<<"$str"
\/tmp\/test\/folder1\/test.txt

И, если вы будете использовать одинарные кавычки вместо двойных, оболочка не изменит двойную \\на единицу, поэтому меньше \будет правильным:

$ sed 's:/:\\/:g'  <<<"$str"
\/tmp\/test\/folder1\/test.txt

Что гораздо чище.

2
27.01.2020, 20:08

Теги

Похожие вопросы