Итерация через аргументы командной строки в Bash

Может ли кто-то быть добрым и объяснить разницу между этими двумя блоками кода? Я бы подумал, что Block # 2 будет выводить то же, что и Block # 1, но это не так. Может кто-нибудь объяснить, почему?

# ./arguments.sh hello my name is X 

Блок № 1

 for i do echo $i done 

Вывод:

 hello my name is X 

Блок №2

 args=$# for (( i=1; i<=$args; i+=1 )) do echo $i done 

Вывод:

 1 2 3 4 5 

Ответ roaima отвечает на вопрос, который вы на самом деле спросили:

В: В чем разница между этими двумя блоками кода? Почему они дают разные результаты?

A: Первый цикл выполняет итерацию по аргументам командной строки; вторая – итерация по номерам аргументов (индексов).

… хотя я полагаю, что вы бы поняли, что многое для себя через шесть-восемь минут – это очевидно. Вероятно, вы хотите, чтобы вторая программа делала что-то вроде

 echo $$i # This doesn't do what you want. 

для отображения аргумента, который индексируется числом, которое хранится в переменной i (и обозначается как $i ). Как отмечается в комментарии, это не делает то, что вы хотите. (Он что-то делает , я рекомендую вам экспериментировать и выяснять, что он делает.) Но это близко к чему-то, что действительно работает:

 eval echo \$$i # Don't do this. 

или, что то же самое,

 eval echo '$'"$i" # Don't do this. 

Эти команды

  • получить значение i (одно из чисел 1, 2, 3, …)
  • придерживайтесь $ перед ним, составляя $1 , $2 , $3 и т. д.
  • используйте команду eval чтобы сказать: «Возьмите эту командную строку, которую я только что создал, и оцените ее, как если бы я ее набрал.

Таким образом, это может повлиять на выполнение

 echo $1 echo $2 echo $3 ︙ 

Но, как следует из комментариев, вы должны стараться избегать этого. eval может быть опасным, если ввод – это что-то другое, кроме простых слов. Поиск по сайту; вы найдете много объяснений.

Но есть достаточно безопасный способ заставить вторую программу сделать то же самое, что и первое: изменить

 echo $i 

в

 echo ${!i} 

ОК, в первую очередь, ${i} в значительной степени совпадает с $i . ! дает эффект, подобный эффекту команды eval${!x} ищет значение x (т. е. $x или ${x} ) и использует это как имя переменной для поиска. Итак, если x=foo , то ${!x} совпадает с $foo . Вышеприведенный код делает то же самое с i , получая параметр, чье имя является значением i .

Кстати, вы всегда должны указывать все ссылки на переменные оболочки (например, "$i" , "$#" , "$args" , "${i}" и "${!i}" ), если у вас нет Не думаю, что это не так, и вы уверены, что знаете, что делаете.

Первый блок выполняет итерацию (неявно) по аргументам командной строки "$@"

 for i in "$@" # same as your "for i" do echo "$i" done 

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

 args=$# # number of command line args for (( i=1; i<=$args; i+=1 )) # loop from 1 to N (where N is number of args) do echo $i done 

Учитывая, что в соответствии с вашим примером $# равно 5, переменная $i примет значения 1 , 2 , 3 , 4 , 5 .

Как указано в другом (теперь удаленном) ответе, вы можете ссылаться на аргументы командной строки по индексу следующим образом:

 args=$# for (( i=1; i<=$args; i++ )) do echo "$i - ${!i}" done