Get specific line from unix command output - unix

lets say I run a command in the shell cmd doSomething and it shows separate lines as output, for example
> cmd doSomething
outputLine1
outputLine2
outputLine3
Is there a way to assign the 2 nd line(outputLine2) in to a variable (e.g testdir) ?
Ideally I would like to be able to use $testdir.

You can combine head and tail, as follows:
doSomething | head -n 2 | tail -n 1
The head -n 2 shows the first two output lines, the tail -n 1 the last of those two.
For putting this into a variable:
variable=$(doSomething | head -n 2 | tail -n 1)

Related

what is the alternate way to count the occurrence of each word without using 'uniq -c' command?

Is it possible to count the occurrence of each word like using uniq -c but with the count after the word rather than before?
Example scenario
Input file named as text1.txt which contain the following data
Renault:cilo:84563
Renault:cilo:84565
M&M:Thar:84566
Tata:nano:84567
M&M:quanto:84568
M&M:quanto:84569
The fields used in the above data are car_company:car_model:customerID
Desired result
cilo 2
Thar 1
nano 1
quanto 2
(car_model and number of cars sold grouped by car_model)
My code
cat test1.txt | cut -d: -f2 | uniq -c
Actual Result
2 cilo
1 Thar
1 nano
2 quanto
Is it possible to do the above process without using uniq -c ,so that I can swap the order of the fields (columns)?
You can use uniq, and simply post-process its output to swap the columns:
cut -d: -f2 test1.txt | uniq -c | awk '{print $2 "\t" $1 "\n" }'
EDIT: Added \n, as noted in a comment.
Save your commands output into a file "badresult";
cat test1.txt | cut -d: -f2 | uniq -c > badresult
Then cut the seventh field and save it into a file named "counts"(you should use space(" ") as a seperator);
cut -d" " -f7 badresult > counts
Then cut the eighth field and save it into a file named "models"(you should use space(" ") as a seperator);
cut -d" " -f8 badresult > models
Now you have your counts and models in seperate files. All you have to do is to show these two files seperately with "pr" command(-m: one file per column, -T:no pre-information)
pr -m -T models counts
Using awk:
cat test1.txt | cut -d: -f2 | uniq -c | awk '{ t = $1; $1 = $2; $2 = t; print }'
The little awk code exchanges fields 1 and 2 using a temporary.
You just need awk for this:
$ awk -F: '{a[$2]++} END {for (i in a) print i, a[i]}' file
cilo 2
quanto 2
nano 1
Thar 1
This goes through every line keeping track of how many times the second field has appeared. Since everything is stored in the array a, then it is just a matter of looping through it and printing its content.

Compare 2 files in unix file1(2M numbers/rows/lines) , file2(2,000,480 numbers/rows/lines)

How can I compare this 2 big files in unix.
I've already tried using 'grep -Fxvf file1.txt file2.txt | wc -l' but the output is 2,000,480 and when switching file1 and file2 the output is 1,999,999.
How can I get the output of '480' because that's what i am expecting.
I've also tried using diff/cmp commands but the output is too complicated.
I think you want an absolute value of a difference in line numbers in 2 files. You can achieve it easily with awk and get a decent result. You'd read numbers of lines in an array and later subtract the array values in the END block. For pure shell it'd have to get more complex. Imagine you get some test data generated (10 and 14 line files):
$ seq 1 10 > ten
$ seq 1 14 > fourteen
And then you do:
$ ( wc -l ten ; wc -l fourteen ) | awk '{ print $1}' | sort -rn | xargs -J % echo % - p | dc
The result:
4
But much better way would be do just do it in 3 lines (get word count for file1, then file2 and then subtract)

Unix Command for counting number of words which contains letter combination (with repeats and letters in between)

How would you count the number of words in a text file which contains all of the letters a, b, and c. These letters may occur more than once in the word and the word may contain other letters as well. (For example, "cabby" should be counted.)
Using sample input which should return 2:
abc abb cabby
I tried both:
grep -E "[abc]" test.txt | wc -l
grep 'abcdef' testCount.txt | wc -l
both of which return 1 instead of 2.
Thanks in advance!
You can use awk and use the return value of sub function. If successful substitution is made, the return value of the sub function will be the number of substitutions done.
$ echo "abc abb cabby" |
awk '{
for(i=1;i<=NF;i++)
if(sub(/a/,"",$i)>0 && sub(/b/,"",$i)>0 && sub(/c/,"",$i)>0) {
count+=1
}
}
END{print count}'
2
We keep the condition of return value to be greater than 0 for all three alphabets. The for loop will iterate over every word of every line adding the counter when all three alphabets are found in the word.
I don't think you can get around using multiple invocations of grep. Thus I would go with (GNU grep):
<file grep -ow '\w+' | grep a | grep b | grep c
Output:
abc
cabby
The first grep puts each word on a line of its own.
Try this, it will work
sed 's/ /\n/g' test.txt |grep a |grep b|grep c
$ cat test.txt
abc abb cabby
$ sed 's/ /\n/g' test.txt |grep a |grep b|grep c
abc
cabby
hope this helps..

UNIX : Substitute value of variable inside script

I may not have got the title perfect for this question, but I am wondering if there is a way to do the following :
Basically, I have a text file with some key value pairs and also a statement (in the same text file) which will be extracted by a shell script and which needs to also simultaneously substitute the A, B, C in the STATEMENT variable .
To make things simple, let me provide an example.
Here is my text file :
File : values.txt
A=1
B=2
C=3
STATEMENT=apple A orange B grape C
Also, I have a shell script which extracts these values and the statement from the text file and uses the STATEMENT variable as a parameter to another script it calls, something like:
Script : first_script.sh
A=`cat values.txt | grep -w '^A' | cut -d'=' -f2`
B=`cat values.txt | grep -w '^B' | cut -d'=' -f2`
C=`cat values.txt | grep -w '^C' | cut -d'=' -f2`
STATEMENT=`cat values.txt | grep -w 'STATEMENT' | cut -d'=' -f2`
second_script.sh $STATEMENT
As you can see, second_script is called from within first_script and it uses the STATEMENT variable, so what I expect to see with the second_script call is :
second_script.sh apple 1 orange 2 grape 3
Note that "A", "B" and "C" should get substituted to their values 1, 2 and 3.
However, what I get is still :
second_script.sh apple A orange B grape C
which is what I don't want.
How do I make sure that A, B and C get substituted to 1,2 and 3 respectively when second_script is called from first_script ?
Sorry to make it confusing.
You can substitute values in a string like this:
STATEMENT=${STATEMENT/A/$A} # replace the letter A with the value of $A
STATEMENT=${STATEMENT/B/$B}
STATEMENT=${STATEMENT/C/$C}
second_script.sh "$STATEMENT"
If you want to match word boundaries use sed:
STATEMENT=$(sed -e "s/\bA\b/$A/g" -e "s/\bB\b/$B/g" -e "s/\bC\b/$C/g" <<< "$STATEMENT")
Also, you don't need to use cat in your script. You can do it like this:
A=`grep -w '^A' values.txt | cut -d'=' -f2`
Or, using awk:
A=$(awk -F= '/^A\y/{print $2}' values.txt)
Alternatively:
Are you able to change the values.txt file?
It would be easier if you could change it to:
A=1
B=2
C=3
STATEMENT="apple $A orange $B grape $C"
Then in your script you could simply import the file like this:
. values.txt
second_script.sh "$STATEMENT"
How about this:
#!/bin/bash
tail -1 values.txt | {
IFS='=' read name statement
./second_script.sh $(echo "$statement" | sed -r $(
head --lines=-1 values.txt | while IFS='=' read name value
do
printf "%s s/\<%q\>/%q/g " "-e" "$name" "$value"
done))
}
I don't like hard coded solutions, so this will read the values.txt file and evaluate all variables (all but last line) in the statement (last line) and give this to the second_script.sh.
I'd be careful about names and values containing special characters like spaces etc. Run thorough test if you plan on using these!

How to interleave lines from two text files

What's the easiest/quickest way to interleave the lines of two (or more) text files? Example:
File 1:
line1.1
line1.2
line1.3
File 2:
line2.1
line2.2
line2.3
Interleaved:
line1.1
line2.1
line1.2
line2.2
line1.3
line2.3
Sure it's easy to write a little Perl script that opens them both and does the task. But I was wondering if it's possible to get away with fewer code, maybe a one-liner using Unix tools?
paste -d '\n' file1 file2
Here's a solution using awk:
awk '{print; if(getline < "file2") print}' file1
produces this output:
line 1 from file1
line 1 from file2
line 2 from file1
line 2 from file2
...etc
Using awk can be useful if you want to add some extra formatting to the output, for example if you want to label each line based on which file it comes from:
awk '{print "1: "$0; if(getline < "file2") print "2: "$0}' file1
produces this output:
1: line 1 from file1
2: line 1 from file2
1: line 2 from file1
2: line 2 from file2
...etc
Note: this code assumes that file1 is of greater than or equal length to file2.
If file1 contains more lines than file2 and you want to output blank lines for file2 after it finishes, add an else clause to the getline test:
awk '{print; if(getline < "file2") print; else print ""}' file1
or
awk '{print "1: "$0; if(getline < "file2") print "2: "$0; else print"2: "}' file1
#Sujoy's answer points in a useful direction. You can add line numbers, sort, and strip the line numbers:
(cat -n file1 ; cat -n file2 ) | sort -n | cut -f2-
Note (of interest to me) this needs a little more work to get the ordering right if instead of static files you use the output of commands that may run slower or faster than one another. In that case you need to add/sort/remove another tag in addition to the line numbers:
(cat -n <(command1...) | sed 's/^/1\t/' ; cat -n <(command2...) | sed 's/^/2\t/' ; cat -n <(command3) | sed 's/^/3\t/' ) \
| sort -n | cut -f2- | sort -n | cut -f2-
With GNU sed:
sed 'R file2' file1
Output:
line1.1
line2.1
line1.2
line2.2
line1.3
line2.3
Here's a GUI way to do it: Paste them into two columns in a spreadsheet, copy all cells out, then use regular expressions to replace tabs with newlines.
cat file1 file2 |sort -t. -k 2.1
Here its specified that the separater is "." and that we are sorting on the first character of the second field.

Resources