Яка мета використання зрушення скриптів оболонки?


118

Я натрапив на цей сценарій:

#! /bin/bash                                                                                                                                                                                           

if (( $# < 3 )); then
  echo "$0 old_string new_string file [file...]"
  exit 0
else
  ostr="$1"; shift
  nstr="$1"; shift  
fi

echo "Replacing \"$ostr\" with \"$nstr\""
for file in $@; do
  if [ -f $file ]; then
    echo "Working with: $file"
    eval "sed 's/"$ostr"/"$nstr"/g' $file" > $file.tmp 
    mv $file.tmp $file
  fi  
done

Яке значення мають рядки, де вони використовуються shift? Я припускаю, що сценарій слід використовувати принаймні аргументи, так що ...?

Відповіді:


141

shiftце bashвбудований вид, який видаляє аргументи з початку списку аргументів. З огляду на , що 3 аргументи, передані сценарієм доступні $1, $2, $3, то виклик shift буде зробити $2новий $1. Зміна shift 2зміниться двома, створюючи нові $1старі $3. Для отримання додаткової інформації дивіться тут:


25
+1 Зауважте, це не обертає їх, а зсуває їх з масиву (аргументів). Shift / unhift і pop / push - загальні назви для цього загального виду маніпуляцій (див., Наприклад, bash's pushdі popd).
золотинки

1
Остаточне посилання: gnu.org/software/bash/manual/bashref.html#index-shift
glenn jackman

@goldilocks дуже правда. Замість того, щоб "повернути", я мав би краще знайти спосіб використовувати інше слово, щось на кшталт "переміщення аргументів вперед у змінних аргументів". У будь-якому випадку я сподіваюся, що приклади в тексті та посилання на посилання дадуть зрозуміти.
humanityANDpeace

Хоча сценарій згадує bash, питання є загальним для всіх оболонок, тому переважає стандарт Posix: pubs.opengroup.org/onlinepubs/9699919799/utilities/…
calandoa

32

Як коментар Золотоволоска і посилання людства описують, shiftперепризначення позиційних параметрів ( $1, $2і т.д.) , так що $1бере на старе значення $2, $2приймає значення $3, і т.д. *   Старе значення $1відкидається. ( $0не змінюється.) Деякі причини цього включають:

  • Це дозволяє вам легше отримати доступ до десятого аргументу (якщо такий є).  $10не працює - інтерпретується як $1об'єднаний з 0 (і може призвести до подібного Hello0). Після a shiftстає десятим аргументом $9. (Однак у більшості сучасних оболонок можна використовувати ${10}.)
  • Як демонструє керівництво Bash для початківців , його можна використовувати для перегляду аргументів. ІМНШО, це незграбно; forнабагато краще для цього.
  • Як і у вашому прикладі сценарію, це легко обробляє всі аргументи однаково, за винятком кількох. Наприклад, в сценарії, $1і $2текстові рядки, в той час як $3і всі інші параметри є іменами файлів.

Тож ось як це виходить. Припустимо, ваш сценарій називається, Patryk_scriptі він називається як

Patryk_script USSR Russia Treaty1 Atlas2 Pravda3

Сценарій бачить

$1 = USSR
$2 = Russia
$3 = Treaty1
$4 = Atlas2
$5 = Pravda3

Оператор ostr="$1"встановлює змінну ostrна USSR. Перше shiftтвердження змінює позиційні параметри так:

$1 = Russia
$2 = Treaty1
$3 = Atlas2
$4 = Pravda3

Оператор nstr="$1"встановлює змінну nstrна Russia. Друге shiftтвердження змінює позиційні параметри так:

$1 = Treaty1
$2 = Atlas2
$3 = Pravda3

І тоді forзміни циклу USSR( $ostr) в Russia( $nstr) в файлах Treaty1, Atlas2і Pravda3.



У сценарію є кілька проблем.

  1. для файлу в $ @; робити

    Якщо сценарій викликається як

    Patryk_script СРСР Російський договір1 "Світовий атлас2" Правда3

    це бачить

    $ 1 = СРСР
    $ 2 = Росія
    $ 3 = Договір1
    $ 4 = Всесвітній атлас2
    $ 5 = Правда3

    але, тому що $@не котируються, простір в World Atlas2не котируються, і forцикл думає , що має чотири файли: Treaty1, World, Atlas2і Pravda3. Це має бути будь-яке

    для файлу у "$ @"; робити

    (цитувати будь-які спеціальні символи в аргументах) або просто

    для файлу

    (що еквівалентно більш тривалої версії).

  2. eval "sed 's /" $ ostr "/" $ nstr "/ g' $ файл"

    Не потрібно, щоб це було eval, і передача неперевіреного вводу користувача на базу evalможе бути небезпечною. Наприклад, якщо сценарій викликається як

    Patryk_script "'; rm *;'" Російський договір1 Атлас2 Правда3

    це виконає rm *! Це викликає велике занепокоєння, якщо сценарій можна запускати з привілеями, вищими, ніж користувачі, які його викликають; наприклад, якщо це можна запустити через sudoвеб-інтерфейс або викликати ним. Це, мабуть, не так важливо, якщо ви просто використовуєте його як у своєму каталозі. Але це можна змінити на

    sed "s / $ ostr / $ nstr / g" "$ файл"

    Це все ще має певні ризики, але вони набагато менш серйозні.

  3. if [ -f $file ], > $file.tmpІ mv $file.tmp $file має бути if [ -f "$file" ], > "$file.tmp"і mv "$file.tmp" "$file", відповідно, для обробки імен файлів , які можуть мати прогалини (або інші забавні символи) в них. ( eval "sed …Команда також манглює імена файлів, у яких є пробіли.)


* shift приймає необов’язковий аргумент: додатне ціле число, яке вказує, скільки параметрів потрібно змістити. За замовчуванням - один ( 1). Наприклад, shift 4причини $5 ставати $1, $6ставати $2тощо. (Зверніть увагу, що приклад у Посібнику з Bash для початківців невірний.) І тому ваш сценарій можна змінити

ostr="$1"
nstr="$2"
shift 2

що може вважатися більш зрозумілим.



Закінчення Примітка / Попередження:

Мова командного рядка Windows (пакетний файл) також підтримує SHIFTкоманду, яка в основному робить те ж саме, що і shiftкоманда в оболонках Unix, з однією разючою різницею, яку я приховую, щоб спробувати не допустити, щоб люди не плутали її:

  • Така команда SHIFT 4- це помилка, яка дає повідомлення про помилку "Недійсний параметр для команди SHIFT".
  • SHIFT /n, де nціле число між 0 і 8, є дійсним - але воно не змінює час . Це зрушує один раз, починаючи з з п -  го аргументу. Тому змушує (п’ятий аргумент) стати , і так далі, залишаючи аргументи від 0 до 3.n SHIFT /4%5%4,  %6%5


2
shiftможна застосувати до while, untilі навіть forпетлі для обробки масивів набагато більш тонкими способами, ніж може простий forцикл. Я часто вважаю його корисним у forциклах, таких як ... set -f -- $args; IFS=$split; for arg do set -- $arg; shift "${number_of_fields_to_discard}" && fn_that_wants_split_arg "$arg"; done
mikeserv

3

Найпростіше пояснення це. Розглянемо команду:

/bin/command.sh SET location Cebu
/bin/command.sh SET location Cebu, Philippines 6014 

Без допомоги shiftви не зможете витягнути повне значення місця, оскільки це значення може бути довільно довгим. Коли ви двічі зміщуєте, виводите аргументи SETта locationвидаляєте так:

x="$@"
echo "location = $x"

Погляньте дуже довго на $@річ. Це означає, що він може зберегти повне розташування у змінну, xнезважаючи на пробіли та коми. Отже, підсумовуючи, ми викликаємо, shiftа потім пізніше отримуємо значення того, що залишилося від змінної $@.

ОНОВЛЕННЯ Я додаю нижче дуже короткий фрагмент, що показує концепцію та корисність shiftбез яких, було б дуже, дуже важко витягти поля правильно.

#!/bin/sh
#

[ $# -eq 0 ] && return 0

a=$1
shift
b=$@
echo $a
[ -n "$b" ] && echo $b

Пояснення : Після shiftзмінна b повинна містити решту матеріалів, що передаються, незалежно від пробілів і т. Д. Це [ -n "$b" ] && echo $bзахист такий, що ми друкуємо лише b, якщо він має вміст.


(1) Це не найпростіше пояснення. Це цікавий кут. (2) Але поки ви хочете об'єднати купу аргументів (або всіх), можливо, вам захочеться ввійти в звичку використовувати замість цього . (3) Я збирався підтвердити вашу відповідь, але я цього не зробив, тому що ви говорите "змініть два рази", але ви насправді не показуєте його в коді. (4) Якщо ви хочете, щоб сценарій міг прийняти багатословне значення з командного рядка, можливо, краще вимагати від користувача ввести все значення в лапки. … (Продовження)"$*""$@"
Скотт

(Продовження) ... Можливо, вам сьогодні все одно, але ваш підхід не дозволяє мати декілька пробілів ( Philippines   6014), провідні пробіли, пробіли чи вкладки. (5) До речі, ви, можливо, також зможете робити те, що ви робите тут, на базі x="${*:3}".
Скотт

Я спробував розшифрувати, звідки ваш коментар "*** не дозволяє декілька пробілів ***" надходить, оскільки це не пов'язано з shiftкомандою. Можливо, ви посилаєтесь на $ @ порівняно з $ * тонкими відмінностями. Але факт залишається фактом, що мій фрагмент вище може точно отримати місце розташування, незалежно від того, скільки пробілів у нього є або задніми, або спереду, це не має значення. Після зсуву розташування міститиме правильне місцеположення.
типологічний

2

shift трактуйте аргументи командного рядка як чергу FIFO, вона щоразу викликає елемент щоразу, коли викликається.

array = [a, b, c]
shift equivalent to
array.popleft
[b, c]
$1, $2,$3 can be interpreted as index of the array.
$# is the length of array
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.