0

I want to copy files that require root to read/write from one system to another. My current solution is to use sudo on each system and use tee as shown.

ssh host sudo cat /etc/somefile | sudo tee /etc/somefile > /dev/null

This works but tee sends it's input to stdout so I have to send tee's ouput to /dev/null.

I looked to the UNIX cat and copy command cp and did not find an answer.
See https://man7.org/linux/man-pages/man1/cp.1.html
and https://man7.org/linux/man-pages/man1/cat.1.html


UPDATED: I now realize that I should have stated that the solution needs to support sudo so the simple solution of using cat won't work.

For example,

ssh host sudo cat /etc/somefile | sudo cat > /etc/somefile

fails because the directory /etc can only be written by root (in my case) and the re-direction to the file > /etc/somefile runs under the current user (who doesn't have access to write to /etc).

2

3 Answers 3

4

There's:

sh -c 'exec cat > file'

Or for arbitrary $files, passed either as environment variables:

sudo FILE="$file" sh -c 'exec cat > "$FILE"'

Or as argument:

sudo sh -c 'exec cat > "$1"' sh "$file"

(where sh goes into $0, and the contents of $file in $1 for that inline script).

(see also >> in place of > to open in append mode, or 1<> to open without truncation (and in read+write mode) to overwrite the file in place, similar to dd's conv=notrunc).

In any case, do not do:

sudo sh -c "exec cat > $file"

As that fails if $file contains any character special in the shell syntax such as space, ;, $... and introduces a command injection vulnerability.


There's also:

dd bs=65536 of=file

(with status=none with the GNU implementation of dd or compatible to suppress the transfer report. There are more options to control how the file is opened, the list of which varies with the dd implementation).


On most systems:

cp /dev/stdin file

There's also moreutils's

sponge file

With GNU sort at least and on text input:

sort -mo file

With text input (or sed implementations that can cope with non-text):

sed -n 'w file'

With text input:

awk '{print > "file"}'

Or with GNU awk:

gawk -v ORS= '{print $0 RT > "file"}'

Here, you could also open the local file as root and then run ssh as the regular user:

sudo zsh -c '
  USERNAME=$SUDO_USER ssh host 'sudo cat /etc/somefile' > /etc/somefile'

That means the data doesn't have to be shoved through an extra pipe.

You could also compress on the fly with xz and decompress on the local end with pixz which supports uncompressing into a file:

ssh host 'sudo xz -0 -c /etc/somefile' | sudo pixz -tdo /etc/somefile
7
  • 1
    I'm not sure I've ever seen you give a answer using dd when either cp or cat would work instead. Is there an equivalent to the useless use of dd?
    – doneal24
    Commented Aug 8, 2022 at 20:37
  • 1
    @doneal24, cat alone can't do it here, you need it combined with sh to open the file for writing. dd can do the opening and read+write loop, cp as well, though /dev/stdin is not fully portable. Commented Aug 8, 2022 at 20:39
  • 2
    @roaima, that's GNU specific and not necessary without a count, dd just transfers along what it reads, short read or not. Commented Aug 8, 2022 at 20:47
  • 1
    @roaima You need iflag=fullblock in cmd | dd bs=somesize count=1 iflag=fullblock of=file if you want to make sure file contains the first somesize bytes of the output of cmd. You don't need it if you omit count to get the full output of cmd into file. Commented Aug 8, 2022 at 20:52
  • 1
    @PatS, try sh -c 'echo "scriptname: $0; 1st arg: $1, 2nd: $2"; will-error-out' A B C D to demonstrate how you pass arguments to an inline script (a script whose contents is passed as the -c argument). Commented Aug 16, 2022 at 15:33
2

If there are multiple files and they need to be in the same places on the destination, tar can copy files, here to a different directory, but the pipe could also involve ssh somehow. Flavor with p to preserve file permissions or maybe some compression flags if ssh isn't doing that for you.

$ mkdir output
$ echo foo > foo
$ echo bar > bar
$ tar cf - foo bar | ( cd output && tar xf - )
$ cat output/*
bar
foo

There may be portability problems if the implementation of tar differs between the systems.

-1

cat is the simplest program that does as you ask. Especially cat -, which tells the command to read data from its standard input.

The left-hand side of your pipe command is unclear about what sudo does with /etc/somefile. Here's my best guess at the answer:

sudo cat /etc/somefile | ssh me@otherhost 'sudo cat - > /etc/somefile'
4
  • 1
    This would require that the shell be running as root, which I don't think is the case for OP. It's for that reason that you can't do something like sudo echo 1 > /proc/sys/some/sysctl/file either.
    – forest
    Commented Aug 8, 2022 at 20:19
  • @forest yes, thank you. I missed that aspect of the question. Edited my answer.
    – Sotto Voce
    Commented Aug 8, 2022 at 20:27
  • In ssh me@otherhost 'sudo cat - > /etc/somefile', /etc/somefile is still opened by me, not root on host. You'd need ssh me@otherhost 'exec sudo sh -c "exec cat > /etc/somefile"' to run a shell as root that opens the file for writing. Commented Aug 8, 2022 at 21:07
  • @SottoVoce, I downvoted because the answer as it is written doesn't work. If you incorporate Stephane Chazelas comment, the answer is acceptable and I'll remove the downvote.
    – PatS
    Commented Aug 17, 2022 at 15:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .