Чи може сценарій оболонки надрукувати свій аргумент, цитуючи так, як ви їх записували в підказці оболонки?


9

У сценарії оболонки моє розуміння полягає в тому, що "$@"розширюється на аргументи сценарію, цитуючи їх за потребою. Наприклад, це пересилає аргументи сценарію до gcc:

gcc -fPIC "$@"

<<<Хоча при використанні синтаксису передачі до stdin bash "@$"не працює, як я очікував.

#!/bin/bash
cat <<< "$@"

Викликаючи сценарій як ./test.sh foo "bar baz"дає

foo bar baz

Я б очікував

foo "bar baz"

Чи є спосіб написати скрипт оболонки, який друкує його аргументи так, як ви писали б їх у підказці оболонки? Наприклад: підказка, яку команду використовувати далі, включаючи аргументи сценарію в підказці.

Відповіді:


5

Ну, "$@"розширюється до списку позиційних параметрів, один аргумент на позиційний параметр.

Коли ви робите:

set '' 'foo bar' $'blah\nblah'
cmd "$@"

cmdвикликається цими 3 аргументами: порожній рядок foo barта blah<newline>blah. Оболонка буде викликати execve()системний виклик приблизно так:

execve("/path/to/cmd", ["cmd", "", "foo bar", "blah\nblah"], [envvars...]);

Якщо ви хочете реконструювати командний рядок оболонки (тобто код на мові оболонки), який би відтворив той самий виклик, ви можете зробити щось на кшталт:

awk -v q="'" '
  function shellquote(s) {
    gsub(q, q "\\" q q, s)
    return q s q
  }
  BEGIN {
    for (i = 1; i < ARGC; i++) {
      printf "%s", sep shellquote(ARGV[i])
      sep = " "
    }
    printf "\n"
  }' cmd "$@"

Або zsh, запитуючи різні типи пропозицій:

set '' 'foo bar' $'blah\nblah'
$ print -r -- cmd "${(q)@}"
cmd '' foo\ bar blah$'\n'blah
$ print -r -- cmd "${(qq)@}"
cmd '' 'foo bar' 'blah
blah'
$ print -r -- cmd "${(qqq)@}"
cmd "" "foo bar" "blah
blah"
$ print -r -- cmd "${(qqqq)@}"
cmd $'' $'foo bar' $'blah\nblah'

Або з zsh, bashабо ksh93(тут для bashYMMV з іншими оболонками):

$ set '' 'foo bar' $'blah\nblah'
$ printf cmd; printf ' %q' "$@"; printf '\n'
cmd '' foo\ bar $'blah\nblah'

Ви також можете скористатися параметром xtrace оболонки, який змушує друкувати оболонку, яку він буде виконувати:

(PS4=; set -x; : cmd "$@")
: cmd '' 'foo bar' 'blah
blah'

Вище ми запустили команду :no-op cmdта параметри позиції як аргумент. Моя оболонка надрукувала їх у приємному процитованому вигляді, що підходить для повторного введення в оболонку. Не всі снаряди так роблять.


5
`"$@"` expands to the script arguments, quoting them as needed

Ні, це не те, що відбувається. Виклик програми потребує списку аргументів, кожен аргумент - це рядок. При запуску програми оболонки ./test.sh foo "bar baz", це створює виклик з трьома аргументами: ./test.sh, fooі bar baz. (Нульовий аргумент - це назва програми; це дозволяє програмам знати, під яким іменем вони називаються.) Цитування - це особливість оболонки, а не функція програмних викликів. Оболонка будує цей список під час здійснення дзвінка.

"$@"безпосередньо копіює список аргументів, переданих до сценарію або функції, до списку аргументів у виклику, де він використовується. Немає жодного цитування, оскільки в цих списках не робиться розбір оболонок.

В cat <<< "$@", ви використовуєте "$@"в контексті , де потрібно один рядок. <<<Operator` потрібно рядок, а НЕ список рядків. У цьому контексті bash приймає елементи списку і з'єднує їх з пробілом між ними.

Для налагодження сценарію, якщо ви запустите set -x( set +xщоб вимкнути), це активує режим слідування, де кожна команда надрукована раніше. В bash, цей слід має лапки, які дозволяють вставити команду назад в оболонку (це не стосується кожної shреалізації).

Якщо у вас є рядок, і ви хочете перетворити його в синтаксис джерела оболонки, який аналізує назад в початковий рядок, ви можете оточити його окремими лапками і замінити кожну цитату всередині рядка '\''.

for x do
  printf %s "'${x//\'/\'\\\'\'}' "
done
echo

Синтаксис заміни рядків має специфіку ksh93 / bash / zsh / mksh. У звичайному ш, вам потрібно перевести петлю на рядок.

for raw do
  quoted=
  while case "$raw" in *\'*) true;; *) false;; esac; do
    quoted="$quoted'\\''${raw%%\'*}"
    raw="${raw#*\'}"
  done
  printf %s "'$quoted$raw' "
done
echo

2

"$@" розширюється на аргументи сценарію, цитуючи їх за потребою

Ну, начебто. Для практичних цілей це повинно бути досить близьким, і довідник посібника говорить про це"$@" is equivalent to "$1" "$2" ...

Отже, з двома параметрами fooі bar baz, це було б однаково:

echo "$@"
echo "$1" "$2"
echo "foo" "bar baz"

(За винятком того, що якщо параметри містили спеціальні символи замість просто простих рядків, вони не розширюватимуться знову після розширення $@та $1...)

Але навіть якщо ми розглянемо $@замінені на параметри в лапках, лапки не було б для того, echoщоб побачити, подібно до того gcc, що цитати також не надходять.

<<<є дещо винятком з правила "$@"== "$1" "$2" ..., чітко згадується, що The result is supplied as a single string to the command on its standard inputпісля розширення параметрів та змінних розширення та видалення цитат серед інших. Отже, як зазвичай, <<< "foo"просто дає fooяк вхід, так само somecmd "foo"дає лише fooаргумент.

Називаючи сценарій як ./test.sh foo "bar baz"[...], я б очікував foo "bar baz"

Якби котирування залишилися, все одно мали би бути "foo" "bar baz". Оболонка або будь-яка запущена команда не має уявлення про те, що було цитування при запуску команди. Або якщо навіть було якесь цитування, про яке потрібно говорити, системний виклик просто отримує список нульових завершених рядків, а лапки є лише особливістю мови оболонки. Інші мови можуть мати інші умови.


0

Альтернативне рішення для баш

q='"'; t=( "${@/#/$q}" ); u=( "${t[@]/%/$q}" ); echo ${u[@]}

Bash не підтримує вкладені заміни, тому завдяки /programming/12303974/assign-array-to-variable#12304017 показав, як перевлаштувати масив. Дивіться man bash( https://linux.die.net/man/1/bash ) для детальної інформації про масиви, розширення та заміну шаблонів (під розширенням параметра).

Аналіз

Bash розміщує параметри командного рядка як масив в $@

q утримує символ цитування.

Подвійні лапки навколо розширення параметрів ${ ... }зберігають окремі параметри як окремі елементи, а загортання їх ( )дозволяє призначити їх як масив змінної.

/#/$qу параметрі розширення замінює початок шаблону (наприклад, регулярний вираз ^) котирувальним знаком.

/%/$qу розширенні параметра замінює кінець шаблону (наприклад, регулярний вираз $) котирувальним знаком.

Використовуйте регістр: запитуйте MySQL на список адрес електронної пошти з командного рядка

Існує кілька змін у наведених вище твердженнях, щоб використовувати іншу котирувальну діаграму, додавати коми між параметрами та знімати остаточну кому. І звичайно мені погано, вводячи пароль у виклику mysql. Тож позов мене.

q="'"; t=( "${@/#/$q}" ); u="${t[@]/%/$q,}"; v="u.email in( ${u%,} )"
mysql -uprod_program -h10.90.2.11 -pxxxxxxxxxxxx my_database <<END
select ...
from users u
join ....
where $v # <<<<<<<<<<<<<<<<<< here is where all the hard work pays off :-)
group by user_id, prog_id
;
END

Зауважте лише, що котирування з подвійними котируваннями не дуже корисно для захисту від косої риски, $розширення та інших подвійних лапок. Ось чому в інших відповідях використовуються одиничні лапки, переходячи на деяку довжину для обробки одинарних лапок всередині рядка, або використовуються власні функції оболонки для створення копії рядка, що котирується.
ilkkachu

@ilkkachu Доречно зазначив! Ось чому (серед інших причин) я підтримав усі попередні відповіді. Також чому я додав цей випадок використання. Сподіваємось, ідеальний не ворог добра.
Джефф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.