#bash #if-statement #tar
#bash #if-statement #tar
Вопрос:
Я новичок в написании bash-скриптов для автоматизации задач, и я пытаюсь отменить идентификацию всех tar-файлов в одном каталоге (их слишком много, чтобы делать это вручную) для множества файлов исходного кода. Все они имеют тип * .tar.gz, *.tar.xz или *.tar.bz2.
Это для установки Linux from Scratch LFS, которую я делаю (я новичок), и я не уверен, как еще автоматизировать эту задачу, кроме как с помощью скрипта bash. Код для моего маленького скрипта для этого приведен ниже.
#!/bin/bash
for afile in 'ls -1'; do
if [ 'afile | grep ".tar.gz"' ];
then
tar -xzf afile
elif [ 'afile | grep ".tar.xz"' ]
then
tar -xJf afile
elif [ 'afile | grep ".tar.xz"' ]
then
tar -xjf afile
else
echo "Something is wrong with the program"
fi
done;
Я ожидал, что он отменит проверку всего в каталоге и создаст отдельные каталоги, но вместо этого он вышел с этой ошибкой:
tar (child): afile: Cannot open: No such file or directory
tar (child): Error is not recoverable: exiting now
tar: Child returned status 2
tar: Error is not recoverable: exiting now
По-видимому, он считает, что файл — это фактический файл, но я не знаю, как изменить файл, чтобы он был каждым файлом, который проходит через мою конструкцию for. Как бы я написал скрипт для этого, тем более что существуют разные типы файлов?
Ответ №1:
Чтобы ваш скрипт работал с минимальными изменениями, используйте $afile
всякий раз, когда вам нужно значение переменной. Знак доллара создает ссылку на переменную; в противном случае вы просто получаете литеральную строку 'afile'
. Также избавьтесь от квадратных скобок и вместо echo
переменной to grep
.
for afile in `ls -1`; do
if echo "$afile" | grep '.tar.gz'
then
tar -xzf "$afile"
elif echo $afile | grep '.tar.xz'
then
tar -xJf "$afile"
elif echo "$afile" | grep '.tar.bz2'
then
tar -xjf "$afile"
else
echo "Something is wrong with the program"
fi
done
Поскольку вы новичок в bash, давайте рассмотрим различные другие способы написания скрипта. Я бы внес пару улучшений. Во-первых, вам не следует зацикливаться ls
. Вы можете получить то же самое, зациклившись *
. Во-вторых, grep
это инструмент с большим весом. Вы можете выполнить несколько простых сравнений строк с помощью встроенных конструкций оболочки, таких как [[
и ==
.
for afile in *; do
if [[ "$afile" == *.tar.gz ]]; then
tar -xzf "$afile"
elif [[ "$afile" == *.tar.xz ]]; then
tar -xJf "$afile"
elif [[ "$afile" == *.tar.bz2 ]]; then
tar -xjf "$afile"
else
echo "Something is wrong with the program"
fi
done
На самом деле, это было бы еще приятнее с case
оператором. Давайте попробуем это. Также давайте отправим сообщение об ошибке в stderr с помощью >amp;2
. Это всегда хорошая идея.
for afile in *; do
case "$afile" in
*.tar.gz) tar -xzf "$afile";;
*.tar.xz) tar -xJf "$afile";;
*.tar.bz2) tar -xjf "$afile";;
*) echo "Something is wrong with the program" >amp;2
esac
done
Мы могли бы даже избавиться от сообщения об ошибке, если бы просто перечислили три типа файлов, которые мы хотим перебрать. Тогда нет способа использовать регистр else.
for afile in *.tar.{gz,xz,bz2}; do
case "$afile" in
*.tar.gz) tar -xzf "$afile";;
*.tar.xz) tar -xJf "$afile";;
*.tar.bz2) tar -xjf "$afile";;
esac
done
Или совершенно другой способ сделать это: используйте find
для поиска всех файлов и его -exec
действие для вызова команды для каждого найденного файла. Здесь {}
находится заполнитель для файлов, которые он находит.
find . -name '*.tar.gz' -exec tar -xzf {} ;
find . -name '*.tar.xz' -exec tar -xJf {} ;
find . -name '*.tar.bz2' -exec tar -xjf {} ;
Комментарии:
1.
for afile in *.tar.{gz,xz,bz2}
Версия имеет потенциальную проблему: она будет запускать цикл с нерасширенным шаблоном для любых расширений, которые не соответствуют каким-либо файлам. Например, если нет файлов .tar.xz, он будет выполняться сafile="*.tar.xz"
. Чтобы избежать этого, вы можете установитьnullglob
(с помощьюshopt -s nullglob
) перед циклом (и отменить его с помощьюshopt -u nullglob
после, чтобы избежать неожиданных побочных эффектов).2.
*
в пустом каталоге возникает та же проблема.