-2

I am trying to read a file using a while loop running as sudo, and then run some awk on each line. File owner is user2 and I am running as user1.

sudo sh -c 'while IFS= read -r line; do
    echo $line | awk '{print $NF}'; done < /home/user2/test.txt'

I am having trouble with the single quotes. Double quotes does not work at both the places where I have single quotes. Is there any way to get the same command to work with some adjustments?

NOTE: I got the output using the following methods:

  • sudo cat /home/user2/test.txt |
    while IFS= read line; do
       echo $line | awk '{print $NF}'
    done
    
  • sudo awk {'print $NF'} /home/user2/test.txt
    

I am trying to understand if there is any solution for using sudo, while and awk with single quotes all of them in a single line.

tripleee
  • 175,061
  • 34
  • 275
  • 318
Saleem
  • 65
  • 1
  • 5
  • Concatenation always works in case of trouble with quotes, replace `awk '{print $NF}'` with `awk '"'"'{print $NF}'"'"'` – Wiktor Stribiżew Jan 18 '21 at 10:59
  • @Saleem : Not really what you asked, but what's the point of your `awk` command? It simply copies its stdin to stdout, so it's useless IMO. – user1934428 Jan 18 '21 at 11:45
  • @user1934428 No, it prints only the last field from each line. – tripleee Jan 18 '21 at 12:29
  • Ah, thanks! Of course! – user1934428 Jan 18 '21 at 12:58
  • As I said in the description, there is an easier and better way for achieving this and i have those solutions, but the original script was written by someone else and I was not able to explain why those quotes are not working like in that script. I was hoping if there is a way to get that working, that would help me fix the gaps in my understanding on using the quotes. – Saleem Feb 09 '21 at 08:29

2 Answers2

1

There are a few reasonable options:

sudo sh -c 'while IFS= read -r line; do echo "$line" | awk '"'"'{print $NF}'"'"';done < /home/user2/test.txt'

or (observing that this particular awk does not require single quotes):

sudo sh -c 'while IFS= read -r line; do echo "$line" | awk "{print \$NF}"; done < /home/user2/test.txt'

or (always a good choice to simplify things) write a script:

sudo sh -c '/path/to/script'

or:

sudo sh << \EOF
while IFS= read -r line; do echo "$line" | 
    awk '{print $NF}'; done < /home/user2/test.txt
EOF

Note that for all of these it would be good to get rid of the loop completely and do:

sudo sh -c 'awk "{printf \$NF}" /home/user2/test.xt'

but presumably this is a simplified version of the actual problem

William Pursell
  • 204,365
  • 48
  • 270
  • 300
1

You should definitely only use sudo for the code which absolutely needs the privileges. This would perhaps be one of the very few cases where a single cat makes sense.

sudo cat /home/user2/test.txt | awk '{ print $NF }'

As others have noted already, the while read loop is completely useless and quite inefficient here, but if you really insist on doing something like that, it's not hard to apply the same principle. (Notice also the fixed quoting.)

sudo cat /home/user2/test.txt |
while IFS= read -r line; do
    echo "$line"   # double quotes are important
done | awk '{ print $NF }'

Tangentially, you cannot nest single quotes inside single quotes. The usual solution to that is to switch one set of quotes to double quotes, and probably also then correspondingly escape what needs escaping inside the double quotes.

sudo sh -c 'while IFS= read -r line; do
       echo "$line" | awk "{print \$NF}"
       # double quotes and^ escape^    ^
    done < /home/user2/test.txt'

Just to spell this out, the double quotes around the Awk script are weaker than single quotes, so the dollar sign in $1 needs to be backslashed to protect it from the (inner) shell.

... But as suggested above, the awk script doesn't need to run inside sudo at all here; and anyway, it's also more efficient to put it outside the done.

tripleee
  • 175,061
  • 34
  • 275
  • 318