Что такое безопасный и портативный способ разделить строку в программировании оболочки?

Попытайтесь использовать виртуальный рабочий стол, это находится на вкладке Graphics "winecfg".

4
08.02.2013, 14:35
3 ответа

Просто набор IFS согласно Вам потребности и позволяют оболочке выполнить разделение слова:

IFS=':'
for dir in $PATH; do
    [ -x "$dir"/"$1" ] && echo $dir
done

Это работает в bash, dash и ksh, но протестированный только с последними версиями.

9
27.01.2020, 20:45
  • 1
    Спасибо!, Как я могу задержать IFS к ее исходным значениям по умолчанию, когда-то обработка сделана? –  rahmu 08.02.2013, 13:43
  • 2
    Nevermind, я храню значение по умолчанию IFS во временной переменной, которая позволяет мне восстанавливать IFS легко. Спасибо за ответ. –  rahmu 08.02.2013, 13:53
  • 3
    Или это или сила оболочка для выполнения данной части кода в отдельном экземпляре оболочки: (IFS=:; for … done). Конечно, это полезно, только если Вам не нужно что-либо позже от того, что было установлено в цикле. –  manatwork 08.02.2013, 14:00
  • 4
    @rahmu: хорошо., но быть осторожным: IFS часто сбрасывается по умолчанию, который не является тем же, как являющимся пустой строкой. При восстановлении его необходимо сохранить то различие. –  ruakh 08.02.2013, 22:43
  • 5
    @ruakh, в то время как это возможно и позволяется POSIX, и имело бы смысл для оболочки иметь $IFS сброс по умолчанию, это не имеет место в любой оболочке, которую я знаю. Всей Границе нравятся оболочки, которые я знаю, имеют IFS=$' \t\n' в их начальной IFS (за исключением zsh который также имеет \0 (так как это может)), –  Stéphane Chazelas 08.02.2013, 23:15

Очевидное решение состояло бы в том, чтобы использовать разделение слова оболочки, но остерегаться нескольких глюков:

IFS=:
set -f
for dir in $PATH; do
    dir=${dir:-.}
    [ -x "${dir%/}/$1" ] && printf "%s\n" "$dir"
done

Вам нужно set -f потому что, когда переменную оставляют неупомянутой, и разделение слова и поколение имени файла (globbing) выполняются на нем, и здесь Вы только хотите разделение слова (например, в маловероятном случае, что $PATH содержит /usr/local/*bin*, Вы хотите это, действительно заглядывают /usr/local/*bin* папка, не в /usr/local/bin и /usr/local/sbin..., и если PATH содержит /*/*/*/../../../*/*/*/*/../../../*/*/*/*, Вы не хотите, чтобы это снизило Вашу машину),

Пустое $PATH компонент означает текущий каталог (.), нет /. $dir/$1 не было бы корректно в этом случае. Работа вокруг должна или записать $dir${dir:+/}$1 или измениться $dir кому: . в этом случае (который дает более полезный вывод при отображении с printf '%s\n' "$dir".

//foo не обязательно то же как /foo, итак, если / находится в $PATH, Вы не хотите $dir/$1, который был бы //$1. Следовательно ${dir%/} разделять запаздывающую наклонную черту.

Затем существует несколько других проблем:

Для $PATH, ":" разделитель полей в то время как для $IFS, это - разделитель полей (да, я знаю, S для Разделителя, вина ksh и POSIX для стандартизации ksh поведения).

Итак, если $PATH /usr/bin:/bin: (который является плохой практикой, но все еще обычно находимый), который означает "/usr/bin", "/bin" и "" (то есть, текущий каталог), в то время как разделение слова оболочки (весь POSIX окружает кроме zsh) разделит это на /usr/bin и /bin только.

Если $PATH установлен, но пуст, который означает: "посмотрите в текущем каталоге только". В то время как оболочки (включая тех, которые рассматривают $IFS как разделитель), развернет его до пустого списка.

Наконец, что не менее важно. Если $PATH сброшен, затем это имеет особое значение, которое является: посмотрите в списке поиска системного значения по умолчанию, который, к сожалению, означает что-то другое в зависимости от того, кого (что команда) Вы спрашиваете.

$ env -u PATH bash -c 'type usbipd'
usbipd is /usr/local/sbin/usbipd
$ env -u PATH ksh -c 'type usbipd'
ksh: whence: usbipd: not found

И в основном, в Вашем сценарии, необходимо было бы предположить то, что тот путь поиска по умолчанию находится в контексте, который имеет значение для Вас.

Обратите внимание, что POSIX оставляет поведение неуказанным когда $PATH сброшен или пуст, так не поможет Вам там. Это также означает, что то, что я сказал выше, не может относиться к некоторым прошлым, текущим или будущим системам POSIX/Unix.

Короче говоря, парсинг $PATH попытаться узнать, куда команда была бы выполнена от, - хитрый бизнес.

Существует стандартная команда для этого, которое является command:

ls_path=$(command -v ls)

Но то, что можно спросить: почему Вы хотите знать?

Теперь на восстановление IFS к ее значению по умолчанию:

oldIFS=$IFS
IFS=:
...
IFS=$oldIFS

будет работать на практике в большинстве случаев, но, как гарантируют, не будет работать POSIX.

Причина - это если $IFS был ранее сброшен, что означает поведение разделения значения по умолчанию (который находится в оболочках POSIX, разделенных на пространстве, вкладке или новой строке), после тех команд, это закончится набор, но пустой (что не означает разделения).

Другая потенциальная проблема состоит в том, если Вы обобщаете тот подход и используете его в большом количестве различных функций, затем если в ... часть выше, Вы вызываете функцию, которая делает то же самое (делает копию $IFS в $oldIFS), затем Вы собираетесь освободить оригинал $oldIFS и восстановите несправедливость $IFS.

Вместо этого Вы могли использовать подоболочки когда возможный:

(
  IFS=:
  ...
)
# only the subshell's IFS was affected, the parent still has its own IFS

Мой подход должен установить $IFS (и поворот set -f на или прочь) каждый раз мне нужно разделение слова (который редок), и не делайте обоих восстановлений предыдущего значения. Конечно, это не работает, если Ваш сценарий называет чужой код, который не следует той практике и принимает поведение разделения слова по умолчанию.

9
27.01.2020, 20:45
  • 1
    Парсинг $PATH был самый простой короткий пример, который я мог придумать. Разделение строки с непробельными разделителями является типичной проблемой, с которой я сталкиваюсь. Я хотел знать, как участники здесь имели дело с ним устойчивым и портативным способом. –  rahmu 08.02.2013, 14:26
  • 2
    Ну, по крайней мере, Вы узнаете, что, если строка заканчивается разделителем, Вы не получите пустой элемент, и что Вам нужно set -f избегать другого побочного эффекта отъезда переменной закрыло кавычки. Многое из этого относится к другим переменным той же формы как $MANPATH, $LD_LIBRARY_PATH... –  Stéphane Chazelas 08.02.2013, 14:38
  • 3
    Да, определенно. Большое спасибо за :) –  rahmu 08.02.2013, 15:22
  • 4
    Может быть лучший путь, но можно различать пустой параметр и параметр сброса путем сравнения ${FOO:-x} и ${FOO-x}. Эти два эквивалентны для параметра сброса, но не пустого параметра. –  chepner 11.03.2013, 20:19
  • 5
    @chepner, общий прием, чтобы сохранить и восстановить IFS, должен записать это oIFS=$IFS; ${IFS+:} unset oIFS и затем то же для восстановления: IFS=$oIFS; ${oIFS+:} unset IFS, но существует все еще проблема, если существуют вложенные функции с помощью того приема. –  Stéphane Chazelas 11.03.2013, 23:08

Если вам нужно прочитать фиксированное количество полей в переменные, вы можете использовать этот метод:

input="age:30"

IFS=':' read -r first_field second_field <<< "$input"

echo "$first_field"
echo "$second_field"

Я нашел это на Вики Грега .

-rговорит read, что не следует рассматривать обратную косую черту как особую.

0
02.08.2020, 07:43

Теги

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