Почему sudo cat дает разрешение отказано, но sudo vim работает нормально?

86

Я пытаюсь автоматизировать добавление источника репозитория в файл pacman.conf моей Arch, но с помощью echoкоманды в моем сценарии оболочки. Однако это не так: -

sudo echo "[archlinuxfr]" >> /etc/pacman.conf
sudo echo "Server = http://repo.archlinux.fr/\$arch" >> /etc/pacman.conf
sudo echo " " >> /etc/pacman.conf

-bash: /etc/pacman.conf: Permission denied

Если я внесу изменения в /etc/pacman.conf вручную с помощью vim, выполнив

sudo vim /etc/pacman.conf

и выйдя из vim с помощью :wq, все работает нормально, и мой pacman.conf был обновлен вручную без жалоб "Permission denied".

Почему это так? И как мне приступить sudo echoк работе? (кстати, я sudo catтоже пытался использовать, но это тоже не удалось с отказом в разрешении)

Кальвин Ченг
источник

Ответы:

51

Проблема в том, что перенаправление обрабатывается вашей исходной оболочкой, а не sudo. Снаряды не способны читать мысли и не знают, что именно >>это предназначено для того, sudoа не для него.

Вам нужно:

  1. процитируйте перенаправление (чтобы оно было передано sudo)
  2. и используйте sudo -s(чтобы sudoиспользовать оболочку для обработки указанного перенаправления.)
гикозавр
источник
1
Это делается в два этапа, например: (1) sudo -s(2) echo "# test" >> /etc/pacman.conf работает. Но можно ли выполнить это одной строкой?
Calvin Cheng,
15
sudo -s 'echo "# test" >>/etc/pacman.conf'это то, что я пытался вам передать.
geekosaur
2
На самом деле, я попробовал это, прочитав ваш ответ выше, но получил странную ошибку вроде этой: - sudo -s 'echo "# test" >> /etc/pacman.conf' /bin/bash: echo "# test" >> /etc/pacman.conf: No such file or directoryвот почему я впоследствии попробовал двухэтапный ручной процесс.
Calvin Cheng
16
Ах ... последняя попытка таким образом сработала -echo 'echo "# test" >> /etc/pacman.conf' | sudo -s
Calvin Cheng
1
как / где узнать об sudoотключении конфигурации -s? визуудо?
Calvin Cheng,
127

Как объяснил @geekosaur, оболочка выполняет перенаправление перед запуском команды. Когда вы набираете это:

sudo foo >/some/file

Текущий процесс оболочки создает свою копию, которая сначала пытается открыть /some/fileдля записи, затем делает этот файловый дескриптор своим стандартным выводом и только после этого выполняется sudo.

Если вам разрешено (конфигурации sudoer часто не позволяют запускать оболочки), вы можете сделать что-то вроде этого:

sudo bash -c 'foo >/some/file'

Но я считаю, что в целом хорошее решение - использовать | sudo teeвместо >и | sudo tee -aвместо >>. Это особенно полезно, если перенаправление - единственная причина, по которой мне sudoв первую очередь нужно ; в конце концов, ненужный запуск процессов от имени root - это именно то, чего sudoнужно избегать. А запускать echoпод root просто глупо.

echo '[archlinuxfr]' | sudo tee -a /etc/pacman.conf >/dev/null
echo 'Server = http://repo.archlinux.fr/$arch' | sudo tee -a /etc/pacman.conf >/dev/null
echo ' ' | sudo tee -a /etc/pacman.conf >/dev/null

Я добавил > /dev/nullв конце, потому что teeотправляет свой вывод как в названный файл, так и в собственный стандартный вывод, и мне не нужно видеть его на моем терминале. (Команда teeдействует как Т-образный соединитель в физическом конвейере, отсюда и получила свое название.) И я переключился на одинарные кавычки ( '... ') вместо двойных ( "... "), так что все было буквально, и я не нужно ставить обратную косую черту перед $in $arch. (Без кавычек или обратной косой черты он $archбудет заменен значением параметра оболочки arch, который, вероятно, не существует, и в этом случае $archзаменяется ничем и просто исчезает.)

Таким образом, мы позаботимся о записи файлов с правами root с использованием sudo. Теперь продолжительное отступление о способах вывода текста, содержащего новую строку, в сценарии оболочки. :)

Что касается BLUF, то, как они говорят, моим предпочтительным решением было бы просто передать здесь-документ в указанную выше sudo teeкоманду; то нет никакой необходимости catили echoили printfили каких - либо других команд на всех. Одиночные кавычки переместились во вводную часть дозорного <<'EOF', но они имеют тот же эффект: тело обрабатывается как буквальный текст, поэтому $archостается в покое:

sudo tee -a /etc/pacman.conf >/dev/null <<'EOF'
[archlinuxfr]
Server = http://repo.archlinux.fr/$arch

EOF

Но пока я бы так поступил, есть альтернативы. Вот несколько:

Вы можете использовать по одному в echoкаждой строке, но сгруппировать их все вместе в подоболочке, поэтому вам нужно добавить в файл только один раз:

(echo '[archlinuxfr]'
 echo 'Server = http://repo.archlinux.fr/$arch'
 echo ' ') | sudo tee -a /etc/pacman.conf >/dev/null

Если вы добавляете -eв echo(и используете оболочку, которая поддерживает это расширение, отличное от POSIX), вы можете вставлять новые строки непосредственно в строку, используя \n:

# NON-POSIX - NOT RECOMMENDED
echo -e '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

Но, как сказано выше, это поведение не определяется POSIX; ваша оболочка может просто отобразить литерал, -eза которым следует строка с кучей литералов \ns. В POSIX это можно сделать, используя printfвместо echo; он автоматически обрабатывает свой аргумент так echo -eже, как и делает, но не добавляет автоматически новую строку в конце, поэтому вы также должны добавить \nтуда дополнительный :

printf '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n \n' | 
  sudo tee -a /etc/pacman.conf >/dev/null

В любом из этих решений то, что команда получает в качестве строки аргумента, содержит двухсимвольную последовательность \n, и сама программа команды (код внутри printfили echo) должна преобразовать это в новую строку. Во многих современных оболочках у вас есть возможность использовать кавычки ANSI $'... ', которые будут переводить последовательности, например, \nв буквальные символы новой строки, прежде чем командная программа когда-либо увидит строку. Это означает, что такие строки работают с любой командой, включая простой old -e-less echo:

echo $'[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

Но, будучи более переносимым, чем echo -e, кавычки ANSI все же не являются расширением POSIX.

И снова, хотя это все варианты, я предпочитаю прямое tee <<EOFрешение, указанное выше.

Марк Рид
источник
Интересно! Не могли бы вы добавить примечание, объясняющее причину, по которой файл также помещается в / dev / null?
knite
sudo bash -c 'foo >/some/file'у меня работает на osx :-)
kayochin
Я предпочитаю этот ответ, потому что он работает с многострочным текстом. cat <<EOF | sudo tee -a /some/file > /dev/null ...
Литва,
По сути, это мой последний ответ, за исключением того, что вы добавили посторонний catпроцесс. :)
Марк Рид
17

http://www.innovationsts.com/blog/?p=2758

Поскольку инструкции выше не так понятны, я использую инструкции из этого сообщения в блоге. С примерами, чтобы было легче понять, что вам нужно делать.

$ sudo cat /root/example.txt | gzip> /root/example.gz -bash
: /root/example.gz: в доступе отказано

Обратите внимание, что это вторая команда (команда gzip) в конвейере, которая вызывает ошибку. Вот тут-то и пригодится наша техника использования bash с параметром -c.

$ sudo bash -c 'cat /root/example.txt | gzip> /root/example.gz '
$ sudo ls /root/example.gz
/root/example.gz

Из вывода команды ls видно, что создание сжатого файла выполнено успешно.

Второй метод похож на первый тем, что мы передаем командную строку в bash, но делаем это в конвейере через sudo.

$ sudo rm /root/example.gz
$ echo "cat /root/example.txt | gzip> /root/example.gz" | sudo bash
$ sudo ls /root/example.gz
/root/example.gz

Нелааро
источник
1
Для такой простой команды может быть лучше использовать sh вместо bash. Например, sudo sh -c 'cat /root/example.txt | gzip> /root/example.gz '. Но в остальном хорошие объяснения, +1.
bryce
7
sudo bash -c 'echo "[archlinuxfr]" >> /etc/pacman.conf'
Пейман Махдиан
источник
1

ШАГ 1 создайте функцию в файле bash ( write_pacman.sh)

#!/bin/bash

function write_pacman {
 tee -a /etc/pacman.conf > /dev/null << 'EOF'
  [archlinuxfr]
  Server = http://repo.archlinux.fr/\$arch
EOF
}

'EOF'не будет интерпретировать $archпеременную.

Исходный файл bash STE2

$ source write_pacman.sh

ШАГ 3 выполнить функцию

$ write_pacman
прайагупд
источник
для чего цитата на EOF?
bilabila 07
0

добавить файлы (sudo cat):

cat <origin-file> | sudo tee -a <target-file>

добавить эхо в файл (sudo echo):

echo <origin> | sudo tee -a <target-file>

(EXTRA) игнорировать вывод:

echo >origin> | sudo tee -a <target-file> >/dev/null
Diogowalterdefreitas
источник