bash 为什么 sudo cat 给出权限被拒绝但 sudo vim 工作正常?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10134901/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 21:56:35  来源:igfitidea点击:

Why sudo cat gives a Permission denied but sudo vim works fine?

bashshellechocat

提问by Calvin Cheng

I am trying to automate the addition of a repository source in my arch's pacman.conf file but using the echocommand in my shell script. However, it fails like this:-

我正在尝试在我的 arch 的 pacman.conf 文件中自动添加存储库源,但echo在我的 shell 脚本中使用该命令。但是,它会像这样失败:-

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

If I make changes to /etc/pacman.conf manually using vim, by doing

如果我使用 vim 手动更改 /etc/pacman.conf,请执行以下操作

sudo vim /etc/pacman.conf

and quiting vim with :wq, everything works fine and my pacman.conf has been manually updated without "Permission denied" complaints.

并退出 vim :wq,一切正常,我的 pacman.conf 已手动更新,没有“权限被拒绝”的抱怨。

Why is this so? And how do I get sudo echoto work? (btw, I tried using sudo cattoo but that failed with Permission denied as well)

为什么会这样?我怎么sudo echo去上班?(顺便说一句,我也尝试过使用,sudo cat但也因权限被拒绝而失败)

采纳答案by geekosaur

The problem is that the redirection is being processed by your original shell, not by sudo. Shells are not capable of reading minds and do not know that that particular >>is meant for the sudoand not for it.

问题是重定向是由您的原始 shell 处理的,而不是由sudo. 贝壳不能读心,也不知道那个特定的东西>>sudo为它准备的,而不是为它准备的。

You need to:

你需要:

  1. quote the redirection ( so it is passed on to sudo)
  2. anduse sudo -s(so that sudouses a shell to process the quoted redirection.)
  1. 引用重定向(所以它被传递给 sudo)
  2. 使用sudo -s(以便sudo使用 shell 处理引用的重定向。)

回答by Mark Reed

As @geekosaur explained, the shell does the redirection before running the command. When you type this:

正如@geekosaur 所解释的,shell 在运行命令之前会进行重定向。当您键入此内容时:

sudo foo >/some/file

Your current shell process makes a copy of itself that first tries to open /some/filefor writing, then makes that file descriptor its standard output, and only then executes sudo.

您当前的 shell 进程会创建一个自己的副本,该副本首先尝试打开/some/file以进行写入,然后将该文件描述符设为其标准输出,然后才执行sudo.

If you're allowed (sudoer configs often preclude running shells), you can do something like this:

如果您被允许(sudoer 配置通常会阻止运行 shell),您可以执行以下操作:

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

But I find a good solution in general is to use | sudo teeinstead of >and | sudo tee -ainstead of >>. That's especially useful if the redirection is the only reason I need sudoin the first place; after all, needlessly running processes as root is precisely what sudowas created to avoid. And running echoas root is just silly.

但我发现一个好的解决方案通常是使用| 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

I added > /dev/nullon the end because teesends its output to boththe named file andits own standard output, and I don't need to see it on my terminal. (The teecommand acts like a "T" connector in a physical pipeline, which is where it gets its name.) And I switched to single quotes ('...') instead of doubles ("...") so that everything is literal and I didn't have to put a backslash in front of the $in $arch.

我说> /dev/null的到底是因为tee将其输出到两个指定的文件自己的标准输出,而我并不需要看到它在我的终端上。(该tee命令就像物理管道中的“T”连接器,这就是它得名的地方。)我切换到单引号 ( '... ') 而不是双引号( "... ") 以便一切都是文字,我没得放一个反斜杠在前面$$arch

So that takes care of writing to files as root using sudo. Now for a lengthy digression on ways to output newline-containing text in a shell script. :)

这样就可以使用sudo. 现在,关于在 shell 脚本中输出包含换行符的文本的方法进行冗长的题外话。:)

First, you can just group all of the echo's together in a subshell, so you only have to do the redirection once:

首先,您可以将所有echo's 组合在一个子外壳中,因此您只需进行一次重定向:

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

Or use printfinstead of echo, so you can embed newlines directly into the string using \n. And need to do so at the end of the string, since printf, unlike echo, doesn't automatically append a newline:

或者使用printf代替echo,这样您就可以使用 将换行符直接嵌入到字符串中\n。并且需要在字符串的末尾这样做,因为printf与 不同echo,不会自动附加换行符:

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

In bash, you can get the same result with echo -e:

在 中bash,您可以获得相同的结果echo -e

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

But most shells will just output the -ewhen you try that, so it's not recommended.

但是大多数 shell-e在您尝试时只会输出,因此不推荐。

With both printfand echo -e, what the command gets as an argument string contains a literal backslash followed by a literal N wherever you type \n, and it's up to the command program itself (the code inside printfor echo) to translate that into a newline. In many modern shells, you have the option of using ANSI quotes $'...', which will translate sequences like \ninto literalnewlines before the command program ever sees the string, which means such strings work with any command whatsoever:

使用printfand echo -e,命令作为参数字符串获取的内容包含一个文字反斜杠,后跟文字 N ,无论您在何处键入\n,都取决于命令程序本身(printfor 中的代码echo)将其转换为换行符。在许多现代 shell 中,您可以选择使用 ANSI 引号$'... ',它会在命令程序看到字符串之前将序列\n转换为文字换行符,这意味着此类字符串可以与任何命令一起使用:

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

But, while more portable than echo -e, ANSI quotes are still a non-POSIX extension.

但是,虽然比 更便携echo -e,但 ANSI 引号仍然是非 POSIX 扩展。

My preferred way of doing this would be to use a here-document and avoid the need for echoor printfentirely:

我的首选方法是使用 here-document 并避免需要echoprintf完全:

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

EOF

回答by nelaaro

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

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

As the instructions are not that clear above I am using the instructions from that blog post. With examples so it is easier to see what you need to do.

由于上面的说明不是很清楚,我正在使用该博客文章中的说明。通过示例,可以更轻松地了解您需要做什么。

$ sudo cat /root/example.txt | gzip > /root/example.gz
-bash: /root/example.gz: Permission denied

$ sudo cat /root/example.txt | gzip > /root/example.gz
-bash: /root/example.gz: 权限被拒绝

Notice that it's the second command (the gzip command) in the pipeline that causes the error. That's where our technique of using bash with the -c option comes in.

请注意,导致错误的是管道中的第二个命令(gzip 命令)。这就是我们将 bash 与 -c 选项一起使用的技术的用武之地。

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

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

We can see form the ls command's output that the compressed file creation succeeded.

我们可以从 ls 命令的输出中看到压缩文件创建成功。

The second method is similar to the first in that we're passing a command string to bash, but we're doing it in a pipeline via sudo.

第二种方法与第一种方法类似,因为我们将命令字符串传递给 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

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

回答by Peyman Mahdian

sudo bash -c 'echo "[archlinuxfr]" >> /etc/pacman.conf'

回答by prayagupd

STEP 1create a function in a bash file (write_pacman.sh)

STEP 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'will not interpret $archvariable.

'EOF'不会解释$arch变量。

STE2source bash file

STE2源 bash 文件

$ source write_pacman.sh

STEP 3execute function

STEP 3执行函数

$ write_pacman