Цикл Bash for со строкой var, содержащей пробелы

Как упоминалось в комментариях AdminBee, скрипт был вызван неправильным образом (с источником контента ).

Правильный способ:

ls -l |./test.awk

Или еще проще, не опираясь на строку #!:

ls -l | awk -f./test.awk
4
08.04.2021, 20:38
4 ответа

Для синтаксического анализа командной строки сделайте так, чтобы операнды пути всегда были последними в командной строке:

./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
5
28.04.2021, 22:53

Если вы используете 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).

9
28.04.2021, 22:53

Между четырьмя опубликованными вами вызовами скриптов есть небольшая разница.

./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может не подходить для этого.

См. также:

Ну и конечно:

для проблем с попыткой работать с несколькими отдельными произвольными строками (именами файлов )в одной переменной.

3
28.04.2021, 22:53

Bash не очень хорошо справляется с переменными, содержащими пробелы.

вы должны использовать функцию «переименования», чтобы помочь вам с этим, как показано ниже:

#используйте ниже, если вы используете систему, подобную Debian

sudo apt -получить установку переименовать

#используйте ниже, если вы используете систему, похожую на красную шляпу

sudo yum установить установить переименовать

#перейдите в каталог, где находятся ваши файлы

cd ваш _целевой _каталог

#переименовать все файлы/переменные, содержащие пробел #с этим шагом проблем с пространством больше не будет

переименовать 's/ / _/g'*

#продолжить без проблем отсюда... наслаждайтесь

-1
28.04.2021, 22:53

Теги

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