Как упоминалось в комментариях AdminBee, скрипт был вызван неправильным образом (с источником контента ).
Правильный способ:
ls -l |./test.awk
Или еще проще, не опираясь на строку #!
:
ls -l | awk -f./test.awk
Для синтаксического анализа командной строки сделайте так, чтобы операнды пути всегда были последними в командной строке:
./myscript -a -b -c -- 'foo bar' 'another file' file[12]
Анализ параметров будет выглядеть примерно так
while getopts abc opt; do
case $opt in
a) a_opt=true ;;
b) b_opt=true ;;
c) c_opt=true ;;
*) echo error >&2; exit 1
esac
done
shift "$(( OPTIND - 1 ))"
for pathname do
# process pathname operand "$pathname" here
done
shift
позаботится о том, чтобы убрать обработанные опции, чтобы в списке позиционных параметров остались только операнды имени пути.
Если это невозможно, разрешите указывать параметр -i
несколько раз и собирайте заданные аргументы в массив каждый раз, когда вы сталкиваетесь с ним в цикле:
pathnames=()
while getopts abci: opt; do
case $opt in
a) a_opt=true ;;
b) b_opt=true ;;
c) c_opt=true ;;
i) pathnames+=( "$OPTARG" ) ;;
*) echo error >&2; exit 1
esac
done
shift "$(( OPTIND - 1 ))"
for pathname in "${pathnames[@]}"; do
# process pathname argument "$pathname" here
done
Это можно назвать
./myscript -a -b -c -i 'foo bar' -i 'another file' -i file1 -i file2
Если вы используете bash
, вы можете использовать для этого массив
#!/bin/bash
files=('foo bar' 'another file' file1 'file2')
for f in "${files[@]}"; do file -- "$f"; done
Для имен файлов, содержащих пробелы, необходимо заключать в кавычки; это необязательно (, но я бы рекомендовал )для простых имен файлов. Если список файлов поступает из текущего каталога, вы можете использовать подстановочные знаки, как и ожидалось, например. files=(*f*)
для соответствия любому файлу или каталогу с f
в его имени. (Но тогда вы, вероятно, могли бы просто использовать for f in *f*; do...done
и полностью избежать массива. )Маркер --
для file
указывает, что любой последующий параметр является именем файла -, даже если оно начинается с дефиса.
Подробнее сman bash
(найдитеArrays
).
Между четырьмя опубликованными вами вызовами скриптов есть небольшая разница.
./script.sh -i "'foo bar' 'another file'"
./script.sh -i "foo\ bar another\ file"
Приведенные выше два параметра передаются сценарию -i
в качестве первого аргумента, а одна строка — в качестве второго. В первом это 'foo bar' 'another file'
, а во втором — foo\ bar another\ file
. На языке оболочкиоба являются допустимыми способами представления двух строк (или имен файлов)foo bar
и another file
. Но обработка кавычек и обратной косой черты применяется только тогда, когда строки находятся в необработанной командной строке, а не когда они находятся внутри переменной, поскольку они заканчиваются в строке.
./script.sh -i foo\ bar another\ file
./script.sh -i 'foo bar' 'another file'
С другой стороны, эти два передают всего три аргумента :-i
, foo bar
и another file
.
Разница несколько важна, так как гораздо проще безопасно работать с отдельными аргументами. Вам просто нужно сохранить их нетронутыми, и вам не нужно обрабатывать встроенные в них кавычки и escape-последовательности.
Кроме того, что важно, запуск чего-то вроде script./*.txt
будет передавать имена файлов как отдельные аргументы.
. это просто вызвало бы file
для обоих файлов, если бы вызывалось какscript 'foo bar' another\ file
:
#!/bin/sh
for f in "$@"; do
file "$f"
done
Но у вас есть и геопты. И с именами файлов в качестве отдельных аргументов только первый будет отображаться как аргумент для -i
. Здесь есть два основных варианта.
Либо попросите пользователя неоднократно использовать параметр -i
, собирая имена файлов в массив, поэтому:
#!/bin/bash
files=()
while getopts "i:" arg; do
case $arg in
i) files+=("$OPTARG");;
esac
done
for f in "${files[@]}"; do file "$f"; done
и запустите как script -i "foo bar" -i "another file"
. (Выполнение script -i file1 file2
будет игнорировать file2
. )Точно так же вы можете добавить еще один массив для сбора имен файлов, заданных с помощью другой опции.
Или задайте параметру «режим», в котором работает скрипт, и возьмите имена файлов в виде списка, отличного от параметров. getopts
оставляет все аргументы нетронутыми, вам просто нужно отбросить те, которые были обработаны с помощью shift
. Так:
#!/bin/bash
while getopts "ie" arg; do
case $arg in
i) mode=i;;
e) mode=e;;
esac
done
shift $(($OPTIND - 1))
if [ "$mode" = i ]; then
for f in "$@"; do file "$f"; done
elif [ "$mode" = e ]; then
echo "do something else with the files"
else
echo "error: invalid mode" >&2
fi
, а затем запустите его как script -i "foo bar" "another file"
.
Я предполагаю, что вы также делаете что-то еще с getopts
, кроме ввода -i
, так как в противном случае вы могли бы просто полностью сбросить флаг. :)Но то, какие другие варианты у вас есть, несколько влияет на то, какое решение будет наиболее разумным (или привычным ).
Также, если ваш цикл вызывает только file
для файлов,вы можете просто запустить file "${files[@]}"
или file "$@"
и пропустить цикл.
Однако, если вы хотите иметь возможность:
script -i foo bar -e doo daa
и если скрипт делает одно для файлов foo
и bar
, а другое для doo
и daa
, тогда это немного другая проблема. Конечно, это можно сделать, но getopts
может не подходить для этого.
См. также:
Ну и конечно:
для проблем с попыткой работать с несколькими отдельными произвольными строками (именами файлов )в одной переменной.
Bash не очень хорошо справляется с переменными, содержащими пробелы.
вы должны использовать функцию «переименования», чтобы помочь вам с этим, как показано ниже:
#используйте ниже, если вы используете систему, подобную Debian
sudo apt -получить установку переименовать
#используйте ниже, если вы используете систему, похожую на красную шляпу
sudo yum установить установить переименовать
#перейдите в каталог, где находятся ваши файлы
cd ваш _целевой _каталог
#переименовать все файлы/переменные, содержащие пробел #с этим шагом проблем с пространством больше не будет
переименовать 's/ / _/g'*
#продолжить без проблем отсюда... наслаждайтесь