Які оператори контролю та перенаправлення оболонки?


245

Я часто бачу підручники в Інтернеті, які з'єднують різні команди з різними символами. Наприклад:

command1 |  command2
command1 &  command2
command1 || command2    
command1 && command2

Інші, здається, з'єднують команди з файлами:

command1  > file1
command1  >> file1

Що це за речі? Як вони називаються? Що вони роблять? Чи є їх більше?


Мета нитка щодо цього питання. .

Відповіді:


340

Вони називаються операторами оболонок, і так, їх більше. Я дам короткий огляд найпоширеніших серед двох основних класів, операторів управління та операторів перенаправлення , а також про те, як вони працюють відносно оболонки bash.

A. Оператори управління

У командній мові оболонки маркер, який виконує керуючу функцію.
Це один із наступних символів:

&   &&   (   )   ;   ;;   <newline>   |   ||

І |&в баш.

A !- це не керуючий оператор, а зарезервоване слово . Це стає логічним НЕ [оператором заперечення] всередині арифметичних виразів і всередині тестових конструкцій (все ще вимагаючи роздільник місця).

A.1 Перерахуйте термінатори

  • ; : Запустить одну команду після закінчення іншої, незалежно від результату першої.

    command1 ; command2

    Спочатку command1виконується запуск, на передньому плані, і як тільки він закінчиться, command2буде запущений.

    Новий рядок, який не знаходиться в прямому рядку або після певних ключових слів, не еквівалентний оператору крапки з комою. Список ;обмежених простих команд все ще є списком - як і в аналізаторі оболонки все ще слід читати в простих командах, які слідують за ;обмеженою простою командою перед виконанням, тоді як новий рядок може розмежовувати весь список команд - або список списків. Різниця є тонкою, але складною: враховуючи, що оболонка не має попереднього обов'язкового для читання даних після нового рядка, новий рядок позначає точку, в якій оболонка може почати оцінювати прості команди, які вона вже прочитала, тоді як ;напівколонка робить ні.

  • & : Це запустить команду у фоновому режимі, що дозволяє продовжувати роботу в одній оболонці.

     command1 & command2

    Тут, command1запускається у фоновому режимі і command2починає працювати відразу на передньому плані, не чекаючи command1виходу.

    Новий рядок після не command1є обов'язковим.

A.2 Логічні оператори

  • && : Використовується для складання списків AND, дозволяє запускати одну команду, тільки якщо інша успішно завершена.

     command1 && command2

    Тут command2буде запущено після command1закінчення і лише в тому випадку, якщо він command1був успішним (якщо його вихідний код був 0). Обидві команди виконуються на передньому плані.

    Ця команда також може бути записана

    if command1
    then command2
    else false
    fi

    або просто, if command1; then command2; fiякщо статус повернення ігнорується.

  • || : Використовується для створення списків АБО, вона дозволяє запускати одну команду, тільки якщо інша невдало завершена.

     command1 || command2

    Тут command2запускається лише у разі command1невдачі (якщо він повернув статус виходу, відмінний від 0). Обидві команди виконуються на передньому плані.

    Ця команда також може бути записана

    if command1
    then true
    else command2
    fi

    або коротше if ! command1; then command2; fi.

    Зауважте, що &&і ||є ліво-асоціативними; див. Пріоритетність логічних операторів оболонки &&, || для отримання додаткової інформації.

  • !: Це зарезервоване слово, яке діє як оператор "не" (але повинен мати роздільник), яке використовується для відмови стану повернення команди - return 0, якщо команда повертає ненульовий статус, повернення 1, якщо воно повертає статус 0 Також логічне НЕ для testутиліти.

    ! command1
    
    [ ! a = a ]

    І справжній НЕ оператор всередині арифметичних виразів:

    $ echo $((!0)) $((!23))
    1 0

A.3 Трубопровідник

  • |: Оператор труби, він передає вихід однієї команди як вхід до іншої. Команда, побудована від оператора труб, називається трубопроводом .

     command1 | command2

    Будь-який вихід, надрукований користувачем command1, передається як вхід до command2.

  • |&: Це скорочення для 2>&1 |bash та zsh. Він передає як стандартний вихід, так і стандартну помилку однієї команди як вхід до іншої.

    command1 |& command2

A.4 Інші розділові знаки списку

;;використовується виключно для позначення кінця заяви справи . Ksh, bash та zsh також підтримують ;&перехід до наступного випадку та ;;&(не в ATT ksh) для продовження та тестування наступних випадків.

(і )використовуються для групування команд та запуску їх у підзарядку. {а }також групуйте команди, але не запускайте їх у підзарядку. Дивіться цю відповідь для обговорення різних типів дужок, дужок і дужок у синтаксисі оболонки.

B. Оператори перенаправлення

Оператор перенаправлення

У командній мові оболонки маркер, який виконує функцію перенаправлення. Це один із наступних символів:

<     >     >|     <<     >>     <&     >&     <<-     <>

Вони дозволяють керувати входом та виходом команд. Вони можуть з’являтися в будь-якому місці простої команди або можуть слідувати команді. Перенаправлення обробляються в порядку, в якому вони з'являються, зліва направо.

  • < : Дає введення для команди.

    command < file.txt

    Зазначене буде виконано commandна вмісті file.txt.

  • <>: те саме, що вище, але файл відкритий у режимі читання + запису замість лише для читання :

    command <> file.txt

    Якщо файл не існує, він буде створений.

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

  • > : Направляє висновок команди у файл.

    command > out.txt

    Вищезгадане збереже вихід commandяк out.txt. Якщо файл існує, його вміст буде перезаписано, а якщо його не існує, він буде створений.

    Цей оператор також часто використовується для вибору, чи слід друкувати щось із стандартною помилкою або стандартним висновком :

    command >out.txt 2>error.txt

    У наведеному вище прикладі >буде перенаправлено стандартний вихід і 2>переспрямується на стандартну помилку. Вихідні дані також можна переспрямовувати, використовуючи, 1>але, оскільки це за замовчуванням, значення 1зазвичай опущено і записується просто як >.

    Таким чином, щоб працювати commandна file.txtі зберегти свій вихід в out.txtі повідомлення про помилки в error.txtвтече:

    command < file.txt > out.txt 2> error.txt
  • >|: Це те ж саме >, але замінить ціль, навіть якщо оболонка була налаштована на відмову від перезапису (з set -Cабо set -o noclobber).

    command >| out.txt

    Якщо out.txtіснує, вихідний commandфайл замінить його вміст. Якщо його не існує, він буде створений.

  • >>: Чи те саме, що >, за винятком випадків, коли цільовий файл існує, нові дані додаються.

    command >> out.txt

    Якщо він out.txtіснує, вихідний commandфайл буде доданий до нього, після того, що вже є в ньому. Якщо його не існує, він буде створений.

  • &>, >&, >>&І &>>: (нестандартні). Перенаправляйте як стандартну помилку, так і стандартний вихід, замінюючи або додаючи відповідно.

    command &> out.txt

    Як стандартна помилка, так і стандартний вихід commandфайлу буде збережено out.txt, перезаписавши його вміст або створивши його, якщо його не існує.

    command &>> out.txt

    Як і вище, за винятком випадків, якщо вони out.txtіснують, вихід і помилка commandбуде додані до нього.

    &>Варіант відбувається в bashтой час як >&варіант походить від CSH (десятиліттями раніше). Вони обидва конфліктують з іншими операторами оболонки POSIX і не повинні використовуватися в портативних shсценаріях.

  • <<: Тут документ. Його часто використовують для друку багаторядкових рядків.

     command << WORD
         Text
     WORD

    Тут commandбуде взято все, поки він не знайде наступне виникнення WORD, Textу прикладі вище, як вхід. Хоча WORDце часто EoFабо його варіанти, це може бути будь-яка буквено-числова (і не тільки) рядок, яка вам подобається. Коли WORDцитується, текст у документі тут обробляється буквально і розширення не виконуються (наприклад, на змінних). Якщо це не буде котируватися, змінні будуть розширені. Більш детально див. Посібник з bash .

    Якщо ви хочете передати висновок command << WORD ... WORDбезпосередньо в іншу команду або команди, вам слід поставити трубу в ту ж лінію << WORD, що і ви не можете поставити її після закінчення СЛОВА або в наступному рядку. Наприклад:

     command << WORD | command2 | command3...
         Text
     WORD
  • <<<: Тут рядки, схожі на документи тут, але призначені для одного рядка. Вони існують лише у порту Unix або rc (там, де він виник), zsh, деяких реалізаціях ksh, yash та bash.

    command <<< WORD

    Все, що дається як WORDрозширено, і його значення передається як вхід command. Це часто використовується для передачі вмісту змінних як введення в команду. Наприклад:

     $ foo="bar"
     $ sed 's/a/A/' <<< "$foo"
     bAr
     # as a short-cut for the standard:
     $ printf '%s\n' "$foo" | sed 's/a/A/'
     bAr
     # or
     sed 's/a/A/' << EOF
     $foo
     EOF

Кілька інших операторів ( >&-, x>&y x<&y) можна використовувати для закриття або копіювання дескрипторів файлів. Детальніше про них див. У відповідному розділі посібника з вашої оболонки ( тут, наприклад, для bash).

Це стосується лише найпоширеніших операторів оболонок Борна. Деякі оболонки мають кілька власних операторів перенаправлення.

Ksh, bash і zsh також мають конструкції <(…), >(…)і =(…)(що останній zshлише в одному ). Це не перенаправлення, а підміна процесу .


2
Напевно, варто було б зауважити, що не всі оболонки рівні, і конкретно підкреслюють особливості башма.
Грег Х'югілл

1
@GregHewgill Так, я побився від цього, сказавши, що я обговорюю з повагою bash. Це вирішується як канонічне запитання і запитання, щоб закрити різні питання "Що робить це дивне штучне", і більшість із них є від користувачів bash. Я сподіваюся, що хтось інший підійде і відповість за негранітні снаряди, але виділення конкретних баш має багато сенсу. Мені доведеться перевірити, я не знаю, які вони з моєї голови.
terdon

&>, >>>І <<<все не-POSIX як посилання не тільки для НЕ-alphanum символів в імені А ось-Дока. Ця відповідь також дуже мало обговорює, як вони працюють - наприклад, майже гірше, ніж марно говорити про просту команду та команду, не пояснюючи, що це таке і як оболонка вирішує.
mikeserv

@mikeserv спасибі Вони працюють на bash і zsh, хоча. Я не знаю, що, якщо що, є справді специфічним для цього списку. Я повинен пройти через це і додати оболонки, в яких працює, але це передбачає з'ясування спочатку.
тердон

1
@ Arc676 Ні, вони не оцінюють справжнє чи хибне, це зовсім інший контекст. Це просто означає, що значення виходу не-0 вказує на проблему (не false), а вихідний код 0 вказує на успіх (не true). Це завжди було так і цілком стандартно. Код виходу без 0 вказує на помилку в будь-якому мені середовищі.
тердон

60

Попередження щодо ">"

Початківці Unix, які щойно дізналися про перенаправлення вводу / виводу ( <і >), часто пробують такі речі

команда ... input_file > the_same_file

або

команда … < файл      > the_same_file

або, майже рівнозначно,

файл котів | команда …> the_same_file

( grep, sed, cut, sort, І spellприклади команд , які люди схильні використовувати в конструкціях , як ці.) Користувачі здивовані, виявивши , що ці сценарії призводять до файлу стають порожнім.

Нюанс, який, мабуть, не згадується в іншій відповіді, можна знайти в першому реченні розділу « Перенаправлення» bash (1) :

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

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

Перенаправлення виводу призводить до відкриття файла… для запису…. Якщо файл не існує, він створюється; якщо він існує, він обрізаний нульовим розміром.

  1. Отже, у цьому прикладі:

    sort roster > roster

    оболонка відкриває rosterфайл для запису, обрізає його (тобто відкидає весь його вміст), перш ніж sortпрограма почне працювати. Природно, нічого не можна зробити для відновлення даних.

  2. Можна наївно цього очікувати

    tr "[:upper:]" "[:lower:]" < poem > poem

    може бути і краще. Оскільки оболонка обробляє перенаправлення зліва направо, вона відкривається poemдля читання (для trстандартного вводу), перш ніж відкривати її для запису (для стандартного виводу). Але це не допомагає. Незважаючи на те, що ця послідовність операцій дає дві ручки файлів, вони обидва вказують на один і той же файл. Коли оболонка відкриває файл для читання, вміст все ще є, але вони все ще заблокуються перед виконанням програми. 

Отже, що з цим робити?

Рішення включають:

  • Перевірте, чи має програма, яку ви запускаєте, власну, внутрішню, можливість визначати, куди йде вихід. На це часто вказують -o(або --output=) маркер. Зокрема,

    sort roster -o roster

    приблизно еквівалентний

    sort roster > roster

    крім того, у першому випадку sortпрограма відкриває вихідний файл. І це не досить , щоб відкрити вихідний файл розумного , поки після того, як вона прочитала все вхідний файл (ів).

    Аналогічно, принаймні деякі версії sedмають опцію -i(редагувати i n місце), яку можна використовувати для запису виводу у вхідний файл (знову ж таки, після того , як всі дані будуть прочитані). Редактори як ed/ ex, emacs, picoі vi/ vim дозволити користувачеві редагувати текстовий файл і зберегти відредагований текст у вихідному файлі. Зауважте, що ed(принаймні) можна використовувати неінтерактивне.

    • viмає пов’язану особливість. Якщо ви введете текст , він запише вміст буфера редагування , прочитає вихід та вставить його в буфер (замінивши вихідний вміст).:%!commandEntercommand
  • Простий, але ефективний:

    командаinput_file > temp_file   && mv temp_file  input_file

    У цьому є недолік, що якщо input_fileпосилання є, воно (ймовірно) буде замінено окремим файлом. Також новим файлом будете володіти вами із захистом за замовчуванням. Зокрема, це несе в собі ризик того, що файл буде прочитаний у всьому світі, навіть якщо оригіналу цього input_fileне було.

    Варіації:

    • commandinput_file > temp_file && cp temp_file input_file && rm temp_file
      що все-таки (потенційно) покине temp_fileсвітоглядний. Навіть краще:
    • cp input_file temp_file && commandtemp_file > input_file && rm temp_file
      Вони зберігають стан посилання, власника та режим (захист) файлу, потенційно ціною вдвічі більше вводу / виводу. (Ви , можливо , буде потрібно використовувати опцію , як -aабо -pна , cp щоб сказати йому , щоб зберегти атрибути.)
    • commandinput_file > temp_file &&
      cp --attributes-only --preserve=all input_file temp_file &&
      mv temp_file input_file
      (розбивається на окремі рядки лише для читабельності) Це зберігає режим файлу (і, якщо ви root, власник), але робить його власником вас (якщо ви не root), і робить його новим, окремий файл.
  • Цей блог ("На місці" редагування файлів) пропонує та пояснює

    {rm input_file   &&   command …> input_file ; } < вхідний_файл

    Це вимагає, щоб commandмати можливість обробляти стандартний вхід (але майже всі фільтри можуть). Сам блог називає це ризикованою суєтою і відштовхує від її використання. І це також створить новий окремий файл (не пов’язаний ні з чим), який належить вам та має дозволи за замовчуванням.

  • У пакеті moreutils є команда під назвою sponge:

    командаinput_file | губка the_same_file

    Дивіться цю відповідь для отримання додаткової інформації.

Ось щось, що стало для мене повним сюрпризом: syntaxerror говорить :

[Більшість з цих рішень] відбудеться збій в файлової системі тільки для читання, де «тільки для читання» означає , що ваш $HOME буде мати права на запис, але /tmpбуде доступний тільки для читання (за замовчуванням). Наприклад, якщо у вас є Ubuntu, і ви завантажилися в консоль відновлення, це зазвичай так. Крім того , оператор тут-документ <<<не працюватиме там , або, так як вона вимагає , /tmpщоб бути для читання / запису , так як він буде писати тимчасовий файл в там.
(пор. це питання включає в себе strace'd вихід)

У цьому випадку може працювати наступне:

  • Для просунутих користувачів: Якщо ваша команда гарантовано зробити таку ж кількість вихідних даних , як є вхід (наприклад, sortчи tr без-d або -sопції), ви можете спробувати
    командаinput_file | dd of = the_same_file conv = notrunc
    Дивіться цю відповідь та цю відповідь для отримання додаткової інформації, включаючи пояснення вищезазначеного, та альтернативи, які працюють, якщо ваша команда гарантовано створює таку ж кількість вихідних даних, як і вхідних даних, або менше (наприклад grep, або cut). Ці відповіді мають ту перевагу, що вони не потребують вільного місця (або вони вимагають дуже мало). Відповіді, наведені вище форми, чітко вимагають, щоб у системі було достатньо вільного місця, щоб мати можливість зберігати весь вхідний (старий) файл та вихідний (новий) файл одночасно; це, очевидно, справедливо і для більшості інших рішень (наприклад, і ). Виняток: напевно, буде потрібно багато вільного місця, оскількиcommandinput_file > temp_file && …sed -ispongesort … | dd …sort Вам потрібно прочитати всі його дані, перш ніж він зможе записати будь-який вихід, і він, ймовірно, буферизує більшість, якщо не всі ці дані у тимчасовому файлі.
  • Тільки для досвідчених користувачів:
    командаinput_file 1 <> the_same_file
    може бути рівнозначно ddвідповіді вище. Синтаксис відкриває файл з ім'ям файлу з дескриптором для введення і виведення , без усічення його - свого роду комбінації і . Примітка. Деякі програми (наприклад, та ) можуть відмовитись від запуску в цьому сценарії, оскільки вони можуть виявити, що вхід і вихід є одним файлом. Дивіться цю відповідь для обговорення вищезазначеного та сценарій, який змушує цю відповідь працювати, якщо ваша команда гарантовано створює таку ж кількість вихідних даних, як і вхідних даних, або менше . Попередження: Я не перевіряв сценарій Петра, тому не поручаю його.n<> filen n<n>catgrep

Отже, про що було питання?

Це була популярна тема щодо U&L; Він розглядається в наступних питаннях:

… І це не враховує Super User або Ask Ubuntu. Я включив у цю відповідь багато інформації з відповідей на вищезазначені питання, але не всю. (Тобто, для отримання додаткової інформації прочитайте вищеперелічені питання та їх відповіді.)

PS Я не маю приналежності до блогу, який я цитував вище.


Оскільки це запитання продовжує виникати, я подумав, що спробував би написати «канонічну відповідь». Чи варто публікувати його тут (і, можливо, посилатись на нього з деяких інших більш важких питань, що торгуються людьми), або я повинен перенести його на одне з питань, що насправді викликає це питання? Також це, можливо, ситуація, коли питання слід об'єднати?
Скотт

/ tmp Каталог, доступний для програм, яким потрібно створити тимчасові файли. Додаток може створювати файли в цьому каталозі, але не повинен вважати, що такі файли зберігаються між викликами програми.
mikeserv

@mikeserv: Так, (1) Я цитую синтаксис, і (2) я сказав, що здивований. Я думав, що якщо щось прочитати-написати, так і буде /tmp.
Скотт

Що ж, те, що сказав @syntaxerror, є вдвічі дивнішим, оскільки, як я думаю, це dashбула б оболонка відновлення за замовчуванням на Ubuntu, і вона не тільки не розуміє <<<херестингу, але й отримує анонімні труби для <<гередокументів і не псується з ${TMPDIR:-/tmp}цим цілі взагалі. Дивіться це чи це для демонстрацій, присвячених обробці документів тут. Також чому однаковий обсяг виходу або менше попередження?
mikeserv

@mikeserv: Ну, dd … conv=notruncі 1<>відповіді ніколи не обрізають вихідний файл, тому, якщо результат команди менший від вхідного (наприклад, grep), в кінці файлу залишиться кілька байтів оригіналу. І, якщо на виході більше , ніж на вході (наприклад, cat -n, nlабо (потенційно) grep -n), є ризик перезапису старих даних , перш ніж ви читали його.
Скотт

29

Додаткові зауваження по ;, &, (і)

  • Зауважте, що деякі команди у відповіді тердона можуть бути нульовими. Наприклад, можна сказати

    command1 ;

    (з ні command2). Це еквівалентно

    command1

    (тобто він просто працює command1на передньому плані і чекає його завершення. Порівняно,

    command1 &

    (без command2) запуститься command1у фоновому режимі, а потім негайно видасть ще одну підказку оболонки.

  • На відміну від цього command1 &&, command1 ||і command1 |не має ніякого сенсу. Якщо ви введете один із них, оболонка (ймовірно) припустить, що команда буде продовжена в інший рядок. Він відобразить вторинну (продовжувальну) підказку оболонки, для якої зазвичай встановлено >, і продовжує читати. У сценарії оболонки він просто прочитає наступний рядок і додасть його до того, що він уже прочитав. (Остерігайтеся: це може бути не тим, що ви хочете статися.)

    Примітка: деякі версії деяких оболонок можуть сприймати такі неповні команди як помилки. У таких випадках (або, фактично, у будь-якому випадку, коли ви маєте довгу команду), ви можете поставити зворотній косу рису ( \) в кінці рядка, щоб сказати оболонці продовжувати читати команду в іншому рядку:

    command1  &&  \
    command2

    або

    find starting-directory -mindepth 3 -maxdepth 5 -iname "*.some_extension" -type f \
                            -newer some_existing_file -user fred -readable -print
  • Як говорить тердон, (і )його можна використовувати для групування команд. Твердження, що вони "не дуже важливі" для цієї дискусії, є дискусійним. Деякі команди у відповіді тердона можуть бути командними групами . Наприклад,

    ( command1 ; command2 )  &&  ( command3; command4 )

    робить це:

    • Біжіть command1і чекайте, коли він закінчиться.
    • Потім, незалежно від результату виконання цієї першої команди, запустіть command2і дочекайтеся її завершення.
    • Тоді, якщо це command2вдалося,

      • Біжіть command3і чекайте, коли він закінчиться.
      • Потім, незалежно від результату виконання цієї команди, запустіть command4і дочекайтеся її завершення.

      Якщо command2не вдалося, припиніть обробку командного рядка.

  • Поза дужки, |зв’язується дуже щільно, так

    command1 | command2 || command3

    еквівалентно

    ( command1 | command2 )  ||  command3

    і &&і ||пов'язують міцніше , ніж ;, так

    command1 && command2 ; command3

    еквівалентно

    ( command1 && command2 ) ;  command3

    тобто command3виконується незалежно від статусу виходу command1та / або command2.


Ідеально, +1! Я сказав, що вони не актуальні, тому що я не хотів вникати в такі деталі. Я хотів відповіді, яка могла б стати швидкою шпаргалкою для новачків, які задаються питанням, які всі дивні хихикання в кінці різних команд. Я не мав на увазі, що вони не корисні. Дякуємо, що додали все це.
terdon

1
Мене хвилює проблема "критичної маси" - якщо ми опублікуємо все, що ми могли б сказати про снаряди, ми закінчимося нашою власною версією TL; DR Посібника з посилання Баша.
G-Man

Також варто згадати: На відміну від мов родини C, ;сама по собі (або без команди, що передує їй) є синтаксичною помилкою, а не порожнім висловом. Таким чином, ; ;є помилка. (Спільна проблема для нових користувачів, IMHO). Також: ;;це спеціальний роздільник для caseтверджень.
муру

1
@muru: Добре, але давайте узагальнимо це. Будь з операторів управління , які можуть виникнути між командами: ;, &&, ||, &, і |, помилки , якщо вони з'являються ні з чим перед ними. Також Тердон звернувся ;;(коротко) у своїй відповіді.
G-Man

1
@Wildcard: Гаразд, я бачу, звідки ти родом. Ключове слово - "може"; все, що я говорив, це те, що я не гарантую, що всі снаряди приймуть такі конструкції (тобто, YMMV). Очевидно, я писав, що раніше я знав про використання linebreakлексеми в граматиці оболонки POSIX. Тож, можливо, можна з упевненістю сказати, що всі сумісні з POSIX оболонки приймуть їх. Я виступаю за свою заяву як загальну відмову; якщо ви знайдете досить стару оболонку до POSIX, таку як фактична оболонка Bourne або старіша, всі ставки знімаються.
G-Man
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.