How can I read a file from last line to first line in shell? [duplicate] - bash

This question already has an answer here:
Read a file backwords line by line in bash
(1 answer)
Closed 5 years ago.
I would like to read a line from last line to first line.
For example:
ant
ball
cat
upto 50 Lines
I need to print it in backwards

Solution 1st: Will be using tac:
tac Input_file
Solution 2nd: In case you don't have tac in your system:
sed '1!G;h;$!d' Input_file
Solution 3rd: With another sed solution too:
sed -n '1!G;h;$p' Input_file
Solution 4th: Using awk too:
awk '{a[FNR]=$0} END{for(i=FNR;i>=1;i--){print a[i]}}' Input_file
Solution 5th: perl solution:
perl -e 'print reverse <>' Input_file

Use tac [file] which is like cat but prints lines backwards.
from tac manual pages:
NAME
tac - concatenate and print files in reverse
SYNOPSIS
tac [OPTION]... [FILE]...

$ echo -e 1\\n2 |
awk '{b=$0 (b==""?"":ORS b)}END{print b}'
2
1
Explained:
$ awk '{b=$0 (b==""?"":ORS b)} # buffer records to the beginning of b
END{print b}' # after all records were buffered print b

Related

Move lines in file using awk/sed

Hi my files look like:
>ID.1
GGAACACGACATCCTGCAGGGTTAAAAAAGAAAAAATCAGTAAAAGTACTGGA
>ID.2
GGAATACCACATCCCGCAGGGTTAAAAAAGAAAAAATCAGTAACAGTACTGGA
and I want to move the lines so that line 1 swaps with 3, and line 2 swaps with 4.
>ID.2
GGAATACCACATCCCGCAGGGTTAAAAAAGAAAAAATCAGTAACAGTACTGGA
>ID.1
GGAACACGACATCCTGCAGGGTTAAAAAAGAAAAAATCAGTAAAAGTACTGGA
I have thought about using cut so cut send the lines into other files, and then bring them all back in the desired order using paste, but is there a solution using awk/sed.
EDIT: The file always has 4 lines (2 fasta entrys), no more.
For such a simple case, as #Ed_Morton mentioned, you can just swap the even-sized slices with head and tail commands:
$ tail -2 test.txt; head -2 test.txt
>ID.2
GGAATACCACATCCCGCAGGGTTAAAAAAGAAAAAATCAGTAACAGTACTGGA
>ID.1
GGAACACGACATCCTGCAGGGTTAAAAAAGAAAAAATCAGTAAAAGTACTGGA
Generic solution with GNU tac to reverse contents:
$ tac -bs'>' ip.txt
>ID.2
GGAATACCACATCCCGCAGGGTTAAAAAAGAAAAAATCAGTAACAGTACTGGA
>ID.1
GGAACACGACATCCTGCAGGGTTAAAAAAGAAAAAATCAGTAAAAGTACTGGA
By default tac reverses line wise but you can customize the separator.
Here, I'm assuming > can be safely used as a unique separator (provided to the -s option). The -b option is used to put the separator before the content in the output.
Using ed (inplace editing):
# move 3rd to 4th lines to the top
printf '3,4m0\nwq\n' | ed -s ip.txt
# move the last two lines to the top
printf -- '-1,$m0\nwq\n' | ed -s ip.txt
Using sed:
sed '1h;2H;1,2d;4G'
Store the first line in the hold space;
Add the second line to the hold space;
Don't print the first two lines;
Before printing the fourth line, append the hold space to it (i.e. append the 1st and 2nd line).
GNU AWK manual has example of swapping two lines using getline as you know that
The file always has 4 lines (2 fasta entrys), no more.
then you might care only about case when number of lines is evenly divisble by 4 and use getline following way, let file.txt content be
>ID.1
GGAACACGACATCCTGCAGGGTTAAAAAAGAAAAAATCAGTAAAAGTACTGGA
>ID.2
GGAATACCACATCCCGCAGGGTTAAAAAAGAAAAAATCAGTAACAGTACTGGA
then
awk '{line1=$0;getline line2;getline line3;getline line4;printf "%s\n%s\n%s\n%s\n",line3,line4,line1,line2}' file.txt
gives output
>ID.2
GGAATACCACATCCCGCAGGGTTAAAAAAGAAAAAATCAGTAACAGTACTGGA
>ID.1
GGAACACGACATCCTGCAGGGTTAAAAAAGAAAAAATCAGTAAAAGTACTGGA
Explanation: store current line in variable $0, then next line as line2, yet next line as line3, yet next line as line4, use printf with 4 placeholders (%s) followed by newlines (\n), which are filled accordingly to your requirement.
(tested in GNU Awk 5.0.1)
GNU sed:
sed -zE 's/(.*\r?\n)(.*\r?\n?)/\2\1/' file
A Perl:
perl -0777 -pe 's/(.*\R.*\R)(.*\R.*\R?)/\2\1/' file
A ruby:
ruby -ne 'BEGIN{lines=[]}
lines<<$_
END{puts lines[2...4]+lines[0...2] }' file
Paste and awk:
paste -s file | awk -F'\t' '{print $3, $4, $1, $2}' OFS='\n'
A POSIX pipe:
paste -sd'\t\n' file | nl | sort -nr | cut -f 2- | tr '\t' '\n'
This seems to work:
awk -F'\n' '{print $3, $4, $1, $2}' OFS='\n' RS= ORS='\n\n' file.txt

Is it possible to pipe head output to sed?

Input file
hello how are u
some what doing fine
so
thats all
huh
thats great
cool
gotcha im fine
I wanted to remove last 4 lines without re directing to another file or say in place edit.
I used head -n -3 input.txt but its removing only the last 2 lines.
Also wanted to understand is it possible to pipe head's output to sed
like head -n -3 input.txt | sed ...
Yes, I went thru sed's option to remove last n lines like below but couldn't understand the nuances of the command so went ahead with the alternative of head command
sed -e :a -e '$d;N;2,5ba' -e 'P;D' file
EDIT: Without creating a temp file solution:
awk -i inplace -v lines=$(wc -l < Input_file) 'FNR<=(lines-4)' Input_file
Could you please try following and let me know if this helps you.
tac Input_file | tail -n +5 | tac > temp_file && mv temp_file Input_file
Solution 2nd: Using awk.
awk -v lines=$(wc -l < Input_file) 'FNR<=(lines-4)' Input_file > temp_file && mv temp_file Input_file

how to replace the first line of a file with the first line of another file [duplicate]

This question already has answers here:
Replace first few lines with first few lines from other file
(3 answers)
Closed 4 years ago.
i have 2 txt files : file1 and file2
i would like to replace the first line of file2 with the first line of file1, with a bash command
file1:
aaaaaaaa
bbbbbbbb
cccccccc
file2:
zzzzzzzz
yyyyyyyy
wwwwwwww
expected result of file2:
aaaaaaaa
yyyyyyyy
wwwwwwww
this can't be done with sed as you don't know what to replace with what...i'm right? so how to do this ?
EDIT:
so in my particular case (i do it in my openwrt router), what worked is :
sed -i "1c $(sed 1q file1)" file2
Thanks to #Sundeed for the link explaining why some commands were only displaying the results in the shell but not writing in the file : https://mywiki.wooledge.org/BashPitfalls#cat_file_.7C_sed_s.2Ffoo.2Fbar.2F_.3E_file
This might work for you (GNU sed):
sed -e '1R file1' -e '1d' file2
Read the first line of file2. Read the first line of file1 and insert it into the output, then delete the first line of file2. Now read and output the rest of file2.
Simply use head and tail for this task:
head -n 1 Input_file1 && tail -n 2 Input_file2
Output will be as follows:
aaaaaaaa
yyyyyyyy
wwwwwwww
You certainly can do this with sed, but why would you?
sed "1c\\
$(sed 1q file1)
" file2
Or with ed
f1="file1";f2="file2";printf "%s\n" '2,$d' "r $f2" '2d' "wq $f2" | ed -s "$f1"

Grep the first number in a line [duplicate]

This question already has answers here:
Printing only the first field in a string
(3 answers)
Closed 6 years ago.
I have a file that has lines of numbers and a file:
2 20 3 file1.txt
93 21 42 file2.txt
52 10 12 file3.txt
How do I use grep, awk, or some other command to just give me the first numbers of each line so that it will only display:
2
93
52
Thanks.
So many ways to do this. Here are some (assuming the input file is gash.txt):
awk '{print $1}' gash.txt
or using pure bash:
while read num rest
do
echo $num
done < gash.txt
or using "classic" sed:
sed 's/[ \t]*\([0-9]\{1,\}\).*/\1/' gash.txt
or using ERE with sed:
sed -E 's/[ \t]*([0-9]+).*/\1/' gash.txt
Using cut is problematic because we don't know if the whitespace is spaces or tabs.
By the way, if you want to add the total number of lines:
awk '{total+=$1} END{print "Total:",total}' gash.txt
You can use this:
grep -oE '^\s*[0-9]+' filename
To handle the leading spaces, I'm currently out of options. You better accept the awk answer.
You can use awk
awk '{print $1}' file

grep "output of cat command - every line" in a different file

Sorry title of this question is little confusing but I couldnt think of anything else.
I am trying to do something like this
cat fileA.txt | grep `awk '{print $1}'` fileB.txt
fileA contains 100 lines while fileB contains 100 million lines.
What I want is get id from fileA, grep that id in a different file-fileB and print that line.
e.g fileA.txt
1234
1233
e.g.fileB.txt
1234|asdf|2012-12-12
5555|asdd|2012-11-12
1233|fvdf|2012-12-11
Expected output is
1234|asdf|2012-12-12
1233|fvdf|2012-12-11
Getting rid of cat and awk altogether:
grep -f fileA.txt fileB.txt
awk alone can do that job well:
awk -F'|' 'NR==FNR{a[$0];next;}$1 in a' fileA fileB
see the test:
kent$ head a b
==> a <==
1234
1233
==> b <==
1234|asdf|2012-12-12
5555|asdd|2012-11-12
1233|fvdf|2012-12-11
kent$ awk -F'|' 'NR==FNR{a[$0];next;}$1 in a' a b
1234|asdf|2012-12-12
1233|fvdf|2012-12-11
EDIT
add explanation:
-F'|' #| as field separator (fileA)
'NR==FNR{a[$0];next;} #save lines in fileA in array a
$1 in a #if $1(the 1st field) in fileB in array a, print the current line from FileB
for further details I cannot explain here, sorry. for example how awk handle two files, what is NR and what is FNR.. I suggest that try this awk line in case the accepted answer didn't work for you. If you want to dig a little bit deeper, read some awk tutorials.
If the id's are on distinct lines you could use the -f option in grep as such:
cut -d "|" -f1 < fileB.txt | grep -F -f fileA.txt
The cut command will ensure that only the first field is searched for in the pattern searching using grep.
From the man page:
-f FILE, --file=FILE
Obtain patterns from FILE, one per line.
The empty file contains zero patterns, and therefore matches nothing.
(-f is specified by POSIX.)

Resources