0

I have a script running to use output from commands that I run using a string from the file that I want to update.

for CLIENT in `cat /home/"$ID"/file.txt | awk '{print $3}'`
do
  sed "/$CLIENT/ s/$/ $(sudo bpgetconfig -g $CLIENT -L | grep -i "version name")/" /home/"$ID"/file.txt >> /home/"$ID"/updated_file.txt
done

The output prints out the entire file once for each line with the matching line in question updated.

How do I sort it so that it only sends the matching line to the new file.

The input file contains lines similar to below:

"Host OS" "OS Version" "Hostname" 

I want to run a script that will use the hostname to run a command and grab details about an application on the host and then print only the application version to the end of the line with the host in it:

"Host OS" "OS Version" "Hostname" "Application Version
R3db3ar
  • 1
  • 1
  • Could the matches happen anywhere in the file, or is your intention to simpily find the lines where the client was in the third field? Then that's yet another antipattern, and another avenue for rather massive logic improvement. – tripleee Sep 02 '20 at 16:12
  • Are you appending the same string, or is `command` some command that changes depending on `CLIENT`? There is almost certainly a simple way to accomplish what you want, and it would help if you could describe what that is. – William Pursell Sep 02 '20 at 16:33
  • The file contains lines similar to below: ** "Host OS" "OS Version" "Hostname" ** I want to run a script that will use the hostname to run a command and grab details about an application on the host and then print only the application version to the end of the line with the host in it: ** "Host OS" "OS Version" "Hostname" "Application Version" ** – R3db3ar Sep 03 '20 at 08:15
  • Do the strings in the file actually contain quotes and spaces within the quotes? – tripleee Sep 03 '20 at 08:26
  • Apologies if the format of the question was off, still learning the ropes. The spaces are there in the file but not the quotation marks. – R3db3ar Sep 03 '20 at 08:59

3 Answers3

1

What you're doing is very fragile (e.g. it'll break if the string represented by $CLIENT appears on other lines or multiple times on 1 line or as substrings or contains regexp metachars or...) and inefficient (you're reading file.txt one per iteration of the loop instead of once total) and employing anti-patterns (e.g. using a for loop to read lines of input, plus the UUOC, plus deprecated backticks, etc.)

Instead, let's say the command you wanted to run was printf '%s' 'the_third_string' | wc -c to replace each third string with the count of its characters. Then you'd do:

while read -r a b c rest; do
    printf '%s %s %s %s\n' "$a" "$b" "$(printf '%s' "$c" | wc -c)" "$rest"
done < file

or if you had more to do and so it was worth using awk:

awk '{
    cmd = "printf \047%s\047 \047" $3 "\047 | wc -c"
    if ( (cmd | getline line) > 0 ) {
        $3 = line
    }
    close(cmd)
    print
}' file

For example given this input (courtesy of Rabbie Burns):

When chapman billies leave the street,
And drouthy neibors, neibors, meet;
As market days are wearing late,
And folk begin to tak the gate,
While we sit bousing at the nappy,
An' getting fou and unco happy,
We think na on the lang Scots miles,
The mosses, waters, slaps and stiles,
That lie between us and our hame,
Where sits our sulky, sullen dame,
Gathering her brows like gathering storm,
Nursing her wrath to keep it warm.

We get:

$ awk '{cmd="printf \047%s\047 \047"$3"\047 | wc -c"; if ( (cmd | getline line) > 0 ) $3=line; close(cmd)} 1' file
When chapman 7 leave the street,
And drouthy 8 neibors, meet;
As market 4 are wearing late,
And folk 5 to tak the gate,
While we 3 bousing at the nappy,
An' getting 3 and unco happy,
We think 2 on the lang Scots miles,
The mosses, 7 slaps and stiles,
That lie 7 us and our hame,
Where sits 3 sulky, sullen dame,
Gathering her 5 like gathering storm,
Nursing her 5 to keep it warm.
tripleee
  • 175,061
  • 34
  • 275
  • 318
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
0

The immediate answer is to use sed -n to not print every line by default, and add a p command where you do want to print. But running sed in a loop is nearly always the wrong thing to do.

The following avoids the useless cat, the don't read lines with for antipattern, the obsolescent backticks, and the loop; but without knowledge of what your files look like, it's rather speculative. In particular, does command need to run for every match separately?

file=/home/"$ID"/file.txt
pat=$(awk '{ printf "\\|$3"}' "$file")
sed -n "/${pat#\\|}/ s/$/ $(command)/p' "$file" >> /home/"$ID"/updated_file.txt

The main beef here is collecting all the patterns we want to match into a single regex, and then running sed only once.

If command needs to be run uniquely for each line, this will not work out of the box. Maybe then turn back to a loop after all. If your task is actually to just run a command for each line in the file, try

while read -r line; do
    # set -- $line
    # client=$3
    printf "%s " "$line"
    command
done <file >>new_file

I included but commented out commands to extract the third field into $client before you run command.

(Your private variables should not have all-uppercase names; those are reserved for system variables.)

Perhaps in fact this is all you need:

while read -r os osver host; do
    printf "%s " "$os" "$osver" "$host"
    command "$host" something something
done </home/"$ID"/file.txt >/home/"$ID"/updated_file.txt

This assumes that the output of command is a well-formed single line of output with a final newline.

tripleee
  • 175,061
  • 34
  • 275
  • 318
0

This might work for you (GNU sed, bash/dash):

echo "command () { expr length \"\$1\"; }" >> funlib
sed -E 's/^((\S+\s){2})(\S+)(.*)/. .\/funlib; echo "\1$(command "\3")\4"/e' file

As an example of a command, I create a function called command and append it to a file funlib in the current directory.

The sed invocation, sources the funlib and runs the command function in the RHS of the substitution command within an interpolation of string, displayed by the echo command which is made possible by the evaluation flag e.

N.B. The evaluation uses the dash shell or whatever the /bin/sh is symlinked to.

potong
  • 55,640
  • 6
  • 51
  • 83