Your script should work if fixed like so:
while
last_line=$(tail -1 "./file.txt")
[[ "$last_line" =~ ^$ ]] || [[ "$last_line" =~ ^[[:space:]]+$ ]]
do
sed -i '$d' "./file.txt"
done
Your script had two main problems: (1) you never updated $last_line
, so the loop's guard would always evaluate the same thing; (2) your [[ "$last_line" =~ $ ]]
test matched any line, since any line has an end. (This is the reason why your script emptied your file completely.) You probably want to match against ^$
instead, which matches only empty lines. Additionally, I simplified the sed
command to delete the last line in the loop's body (simply $d
does the job).
However, this script is unnecessarily complicated. sed
is there for just that kind of thing! This one-liner will do the same thing as the above script:
sed -i ':a;/^[ \n]*$/{$d;N;ba}' ./file.txt
Roughly,
- Match current line against
^[ \n]*$
. (i.e, can only contain whitespaces and newlines)
- If it doesn't match, just print it. Read in next line and continue with step 1.
- If it does match,
- If we are at the end of the file, delete it.
- If we are not at the end of the file, append the next line to the current line, inserting a newline character between the two, and go back to step 1 with this new, longer line.
There are lots of awesome sed
tutorials on the Internet. For example, I can recommend this one. Happy learning! :-)
Update: And of course, if you additionally want to remove the last (non-blank) line of the file after having truncated the trailing blank lines, you can just use another sed -i '$d' ./file.txt
after either your script or the above one-liner. I intentionally did not want to include that in the sed
one-liner since I thought that removing trailing blank lines is quite a reusable piece of code that may be interesting for other people; but removing the last non-blank line is really specific to your use case, and trivial anyway once you removed the trailing blank lines.
Here's a sed
approach:
$ sed -nE '1s/.{11}(.{8}).*/\1/p; 3s/.{3}(.{4}).*/\1/p' file
Ethernet
t6 a
Explanation
The -n
suppresses normal output (normal is to print every input line) so that it only prints when told to. The -E
enables extended regular expressions.
The sed
script has two commands, both using the substitution operator (s/original/replacement/
). The 1s/.{11}(.{8}).*/\1/p
will only run on the 1st line (that's what the 1s
does), and will match the 1st 11 characters of the line (.{11}
), then it captures the next 8 ((.{8})
, the parentheses are a "capture group") and then everything else until the end of the line (.*
). All this is replaced with whatever was in the capture group (\1
; if there were a second capture group, it would be \2
etc.). Finally, the p
at the end (s/foo/bar/p
) causes the line to be printed after the substitution has been made. This results in only the target 8 characters being output.
The second command is the same general idea except that it will only run on the 3rd line (3s
) and will keep the 4 characters starting from the 4th.
You could also do the same thing with perl
:
$ perl -ne 'if($.==1){s/.{11}(.{8}).*/\1/}
elsif($.==3){s/.{3}(.{4}).*/\1/}
else{next}; print; ' file
Ethernet
t6 a
Explanation
The -ne
means "read the input file line by line and apply the script given by -e
to each line. The script is the same basic idea as before. The $.
variable holds the current line number so we check if the line number is either 1
or 3
and, if so, run the substitution, else skip. Therefore the print
will only be run for those two lines since all others will be skipped.
Of course, this is Perl, so TIMTOWTDI:
$ perl -F"" -lane '$. == 1 && print @F[11..19]; $.==3 && print @F[3..6]' file
Ethernet
t6 a
Explanation
Here, the -a
means "split each input line on the character given by -F
and save as the array @F
. Since the character given is empty, this will save each character of the input line as an element in @F
. Then, we print elements 11-19 (arrays start counting at 0
) for the 1st line and 3-7 for the 3rd.
Best Answer
You can read both files together line by line to get your desired output. ( I assume you don't have any other unwanted lines in these files )
users.txt
is read using standard input file descriptor0
mails.txt
is read using our given file descriptor3
Output: