сложные функции bash: правильное использование? - PullRequest
1 голос
/ 31 июля 2011

Я заинтересован в автоматизации повседневных задач.До недавнего времени каждая часть одного из моих сценариев работала без сбоев.Но теперь я пытался реализовать zenity и все развалилось.Теперь я надеюсь, что вы можете посмотреть, что я делаю не так.

Су, подхожу к вопросу: у меня жестокий сценарий bash, похожий на этот:

#!/bin/bash

dosomething () {
  # code
  echo $1 >> ~/test.txt # For debugging
  $1 & # ← Important line
  # more code
}

main () {
  # other code
  dosomething "/usr/bin/rsync $rsync_options" # ← Call it "do_1"
  dosomething "/usr/bin/find $findme_path -iname \"*.gpx\" -print0 | xargs -0 $other_command" # ← Call it "do_2"
  # another code
}

( main | (zenity --progress $zenity_options || $still_another_command ) &

Что (должно) случиться: вывод main передается в zenity (индикатор выполнения).На самом деле main не выполняет никакой команды, но вызывает dosomething с параметром, который содержит команду для выполнения.dosomething выполняет команду.

Что на самом деле происходит: «эхо» часть dosomething работает так, как ожидалось.После выполнения скрипта команды в do_1 и do_2 правильно отображаются в ~ / test.txt.(Если я скопирую и вставлю содержимое ~ / test.txt в свой терминал, каждая команда будет выполнена с ожидаемыми результатами.)«Важная строка» «do_1» выполняется с ожидаемыми результатами.Но «Важная строка» в «do_2», кажется, не имеет никакого эффекта.По крайней мере, я не вижу эффектов $ other_command после выполнения скрипта.

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

Ответы [ 2 ]

1 голос
/ 01 августа 2011

Краткий ответ: см. BashFAQ # 50 .

Длинный ответ: когда bash анализирует строку, он анализирует кавычки и разделители команд (| и т. Д.) Перед выполнением подстановки переменной;в результате он запускает $1 & в функции, кавычки и труба в значении $ 1 никогда не анализируются, они просто передаются команде (в данном случае usr / bin / find) как частьАргумент.Чистый результат: на самом деле он работает в эквиваленте /usr/bin/find $findme_path -iname '"*.gpx"' -print0 '|' xargs -0 $other_command".

Обычно, в подобных случаях, я бы рекомендовал передать команду в виде последовательности слов (т.е. запустить функцию "$@" & и вызватьэто как dosomething /usr/bin/find $findme_path -iname "*.gpx" -print0, но даже это не будет обрабатывать канал в команде - это все равно будет рассматриваться как еще один аргумент.

Одна из возможностей - использовать eval. Этого следует избегать, есливообще возможно, потому что eval - это хороший способ создания ошибок сценариев - массивных ошибок, скрытых ошибок, непонятных ошибок, ошибок безопасности ... Он добавляет дополнительный уровень синтаксического анализа к всему в командестрока, которая означает, что, например, если вы пытаетесь оперировать файлом с именем Fred's file.txt, он примет этот апостроф в качестве кавычки и будет очень запутан. Работа с файлами, в которых есть обратные кавычки, слишкомужасно созерцать. В основном, это плохие новости.

После быстрого взгляда на реальный сценарий я бы порекомендовал сочетание стратегий: скрыть как можно большеОн задает сложность в функциях оболочки, поэтому вы не пытаетесь передать каналы и перенаправления в функцию dosomething, а затем используете подход с последовательностью слов, который я упоминал ранее.Для сценария, о котором идет речь, я бы сделал:

#!/bin/bash

dosomething () {
  # code
  printf "%q " "$@" >> ~/test.txt # this gives a much better idea what's being done than echo $1 would
  "$@" &
  # more code
}

# hide the pipeline in a shell function
find_and_do_something () {
  /usr/bin/find "$1" -iname "*.gpx" -print0 | xargs -0 $other_command
}

main () {
  # other code
  dosomething /usr/bin/rsync $rsync_options
  dosomething find_and_do_something "$findme_path"
  # another code
}

( main | (zenity --progress $zenity_options || $still_another_command ) &

Это означает, что в вашем файле журнала не будет сведений о выполняемом канале, просто find_and_do_something /whatevers/in/findme_path, но, по крайней мере, он будетработа.

0 голосов
/ 31 июля 2011

Где вы определяете $other_command? Также обратите внимание, что в конце отсутствует пропущенная конечная скобка (main открывает не закрытую скобку).

...