0

I have a issue with for loop. Will brief it out for you.

$ for i in `cat serverss`;do ssh -q $i sudo echo "a b c" > /tcb/files/auth/x/xyz;done
ksh: /tcb/files/auth/x/xyz: cannot create

I am getting above error whenever i try to create or modify a file. serverss is the file file which has list of servers.We have sudo to root on those systems.

The problem is when i run echo "a b c" > /tcb/files/auth/x/xyz on individual servers it runs perfectly. What is the issue in my for loop which throws me the above error.

Jim Lewis
  • 43,505
  • 7
  • 82
  • 96
salman
  • 3
  • 2
  • It's not an issue with the `for` loop itself at all; it's an issue with how the argument to `ssh` is given. You'd have the same problem with `ssh -q $i sudo echo "a b c" > /tcb/files/auth/x/xyz` not inside any `for` loop at all. – Charles Duffy Mar 27 '17 at 16:17
  • Another thing is that `sudo echo` is useless: it escalates the privileges of `echo` (which is just writing to stdout), **not** the privileges of the shell opening `/tcb/files/auth/x/xyz`. – Charles Duffy Mar 27 '17 at 16:17

2 Answers2

1

Your command is getting broken. It executes sudo echo "a b c" remotely and tries to redirect the standard output to the local file /tcb/files/auth/x/xyz which does not exist.

You should enclose it somehow, for example:

$ for i in `cat serverss`;do ssh -q $i "sudo echo 'a b c' > /tcb/files/auth/x/xyz";done

Or as @CharlesDuffy would probably prefer:

$ while read -r line; do ssh -q $line "sudo echo 'a b c' > /tcb/files/auth/x/xyz";done < serverss
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Julen Larrucea
  • 149
  • 1
  • 9
  • This does violate [DontReadLinesWithFor](http://mywiki.wooledge.org/DontReadLinesWithFor), but then, the original did too. – Charles Duffy Mar 27 '17 at 16:15
  • BTW, no, that's *not* as I'd prefer; all-caps variable names are [reserved by POSIX convention](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html) for variables with meaning to the operating system or shell. (Keep in mind that setting a regular shell variable will overwrite any like-named environment variable) – Charles Duffy Mar 27 '17 at 16:24
  • I had tried putting double quotes as mentioned for i in `cat serverss`;do ssh -q $i "sudo echo 'a b c' > /tcb/files/auth/x/xyz";done. But still no luck. same error – salman Mar 27 '17 at 16:24
  • a few notes. Inside a `while read` loop, you'll want ` – Charles Duffy Mar 27 '17 at 16:25
  • and I note that the value is being assigned to a variable named `LINE` but then read from a variable named `i`. – Charles Duffy Mar 27 '17 at 16:25
  • @CharlesDuffy: You are right, I just wanted to propose a solution with minimum editing and then something better. But... Wow! You are fast typing! ;) – Julen Larrucea Mar 27 '17 at 16:26
  • @salman, not quite the same error -- putting it in quotes like that meant you were getting the error from the *remote* machine (because `sudo` didn't apply to the redirection) instead of the *local* machine (because the redirection was happening locally). So it did improve things somewhat. – Charles Duffy Mar 27 '17 at 16:26
0

There are two issues here:

  • You're opening /tcb/files/auth/x/xyz locally, not on the remote system.
  • sudo echo foo > bar does not provide extra privileges to help in opening bar.

This latter is because something > output opens output before it runs something. Thus, sudo hasn't even been started when /tcb/files/auth/x/xyz is opened!

Instead, consider:

ssh -q "$i" 'echo "a b c" | sudo tee /tcb/files/auth/x/xyz >/dev/null'

The first problem is solved because the redirection is inside the string passed to ssh to be run by a remote shell. (If you pass ssh a bunch of separate arguments, it just combines them with spaces to form such a string; it's better to form the string yourself, and thereby have more control).

The second problem is solved by forcing /tcb/files/auth/x/xyz to be opened by tee, after tee has had its privileges escalated by sudo.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Thank you Charles it worked. Actually i tried to use tee in one my sed substitution like ssh -q abc "sed -e 's/\:u_pwd.*/\:u_pwd\=np\:\\/p' /tcb/files/auth/a/abcde.tmp | sudo tee /tcb/files/auth/a/abcde >/dev/null" it empty my file as you are aware that sed -i option doesnt work in KSH i make a copy of the file do sed on it and direct it to orignal file like this sed -e 's/\:u_pwd.*/\:u_pwd\=np\:\\/p' /tcb/files/auth/a/abcde.tmp >/tcb/files/auth/a/abcde. My point is for echo you suggested to use tee instead of > so for sed what can i use as tee doesnt seem to do my job. – salman Mar 28 '17 at 09:32
  • It's actually not ksh-vs-bash; whether `sed -i` works depends on which version of `sed` you have, not which shell you use. That said, since you're already using different filenames for input and output, that's as it should be -- I'd suggest making sure the `sed` command works as-intended. Does `ssh -q abc "sed -e 's/\:u_pwd.*/\:u_pwd\=np\:\\/p' /tcb/files/auth/a/abcde.tmp"` really have the output you intend? If not, I'd suggest using `set -x` -- as in, `ssh -q abc "set -x; sed -e 's/\:u_pwd.*/\:u_pwd\=np\:\\/p' /tcb/files/auth/a/abcde.tmp"`` -- to debug. – Charles Duffy Mar 28 '17 at 13:18
  • yes my sed works i have tested on many servers. sed 's/\:u_pwd.*/\:u_pwd\=NP\:\\/p' /tcb/files/auth/a/abcde.tmp > /tcb/files/auth/a/abcde. The point is Sir above sed command runs on the individual server properly but when added in a for loop for list of servers it fails. it throws following error (ksh: /tcb/files/auth/a/abcde: cannot create) . i had double quotes for command also but no luck. As for now i am able to create files using your suggested tee command with echo but i am not sure regarding modifying any files in for loop. Thank you – salman Mar 28 '17 at 14:19
  • Not just whether the `sed` works, but whether it works *behind ssh* in the specific manner in which you quoted it. I don't trust the quoting given in your comment much -- you're escaping characters that don't *need* to be escaped; sometimes that can have unfortunate side effects, depending on which extensions to the standard have been implemented. – Charles Duffy Mar 28 '17 at 15:27
  • Hello Sir you were right the behavior of SED is quite different on SSH. As per your advice i reworked on my sed substitution and came up with ssh -q abc sed -e 's/:u_pwd.*/:u_pwd=Lp:\\/g' /tcb/files/auth/a/abcde | sudo tee /tcb/files/auth/a/abcde which throws me error sed: Function s/:u_pwd.*/:u_pwd=Lp:\/g cannot be parsed here in the error. what i noticed here is the second \ not used by the sed in SSH and throws the error for the same. – salman Mar 29 '17 at 15:59
  • The appropriate thing to do here is to let the shell quote your working `sed` command for you, instead of doing it by hand -- or to pass it on stdin as a quoted literal heredoc. That latter approach is discussed in http://stackoverflow.com/questions/30234546/running-shell-command-that-has-nested-quotes-via-ssh; the former in http://stackoverflow.com/a/42537289/14122 – Charles Duffy Mar 29 '17 at 16:09