How can I display the longest line entered into a Bash prompt? - bash

I am trying to figure out a way to display the longest line entered from user inputs.
For example, my script so far will have the user input in 4 lines:
Hello
Hello will
Hello will turner
Hello will turner honey
In my code I have:
echo "Please enter 4 lines:"
read LINE1
read LINE2
read LINE3
read LINE4
I am wondering if there is a way for me to count each of my lines and then output the biggest one. Making a file would probably be easiest but I wanted to know if I could just use the Bash commands to do so.

If you are only interested in the longest line then you can use a loop and compare the current line with the next line you read:
#!/bin/bash
max=0
for((i=0;i<4;i++)); do
read -r line
len=${#line}
if [[ len -gt max ]] ; then
max=$len
long="${line}"
fi
done
echo longest line="${long}" length="${max}"
If you want to keep the other lines, then you can use an array and apply the same logic on the array.

Assumptions
You don't explain what you expect to happen when two or more lines are the same length. I therefore make the assumption that when two lines are the same length, one should store and report the newer line as "longest."
Compare Line Lengths with Bash Expansions
A more idiomatic (and in my opinion clearer) way of doing this with Bash 4.x would be:
#!/usr/bin/env bash
# Guard against exported environment variables.
unset longest_line
for line in {1..4}; do
read -p "Enter line $line: "
(( "${#REPLY}" >= "${#longest_line}" )) && longest_line="$REPLY"
done
echo "$longest_line"
This uses a variety of shell expansions, the read builtin's -p flag to prompt, read's default REPLY variable to hold the result, and a line-length comparison against the longest line seen so far to perform the key task.
In this example, you don't even have to initialize a value for longest_line since the length of an unset variable is zero, but it's a good defensive programming practice not to rely on the variable being unset. If you prefer to actually set the initial state of the variable yourself, you can set it to the empty string instead with longest_line=''.
The code above will generate the expected result:
$ bash longest_line.sh
Enter line 1: foo
Enter line 2: bar
Enter line 3: foo bar
Enter line 4: baz
foo bar

Related

Receive values directly from the command line

What I'm attempting to do is receive values from the command line (instead of using the read method and asking the user to enter the values and/or file names in multiple steps).
./hello.sh 5 15 <file_name.txt
I have heard that simply using an array can help do the same, but I am not able to-
Avoid printing
5 15
on the next line
Since 5 and 15 are being printed, I'd expect the string 'abcdefgh' (contents of file_name.txt) to be printed; however, the output stops at
5 15
I would really appreciate it if someone could point out why my code isn't sufficient, and if possible, point me in the direction of some learning resources to broaden my knowledge of this concept.
Here is the code:
#! /usr/bin/bash
echo "$#"
I am simply testing things out (wanted to print out the variables before doing anything with and to them).
<file_name.txt is a redirection. It is not passed as a parameter. The parameters of the script are 5 and 15. The < redirects the file file_name.txt to standard input stdin of the script. You can read from stdin with for example cat.
#!/usr/bin/bash
echo "$#" # outputs parameters of the script joined with spaces
cat # redirects standard input to standard output, i.e. reads from the fiel
why my code isn't sufficient
Your script is not reading from the file, so the content of the file is ignored.
point me in the direction of some learning resources
File descriptors and redirections and standard streams are basic tools in shell - you should learn about them in any shell and linux introduction. My 5 min google search resulted in this link https://www.digitalocean.com/community/tutorials/an-introduction-to-linux-i-o-redirection , which looks like some introduction to the topic.
Will this work?
./hello.sh 5 15 `catfile_name.txt`
And update hello.sh to:
#! /usr/bin/bash
shift 2
echo $#
Here is a more generic solution. It looks at each input parameter in turn. If it is a valid file, it outputs the contents of the file. Otherwise if just prints the parameter.
#! /usr/bin/bash
for $parameter in "${#}"; do # Quotation marks avoid splitting parameters with spaces.
if [ -f $parameter ]; then # '-f {value}' tests if {value} is a file.
cat $parameter
else
echo $parameter # You could also use 'echo -n ...' to skip newlines.
fi
done

How to read a file line by line into a variable without a loop

So you know this right?:
Read a file line by line assigning the value to a variable
How do you do this without a loop, I'm in a situation where loops would case errors and a mess.(If you've ever tried messing with parallel) But I still need to read a file line by line.
Is there some way of doing it without a loop? xargs? awk? parallel?
Use readarray to read the whole file into an array variable. Each line will be an array element.
readarray -t arrayname < filename
The question really doesn't make much sense to me, but you can certainly do:
{
read line_one <&3; # Read from the input file
echo "The first line is $line_one"
read line_two -u 3; # A different method to read
echo "The second line in $line_two"
...
} 3< input file
Note that it's not really necessary to read from fd 3, but it's much safer if your commands are not reading from the same input as the reads.
But if you're truly "in a situation where loops would case errors and a mess" then you have a problem for which this is not the solution.
FWIW, using a recursive function (well, recursion might be a loop...). First some test material:
$ seq 1 4 > file
then the function fun:
$ fun() {
if read -r line <&5 # read from defined FD 5
then
echo process $line # process the lines here
fun # recursive function call
fi
}
calling for fun:
$ exec 5< file
$ fun
process 1
process 2
process 3
process 4
$ exec 5<&- # close the FD
It is a bit unclear what you are asking. Maybe you are simply asking for:
myvar="$(cat thefile)"

Bash: Loop through lines of a file and assign line to numbered variable names

I intend to read the lines of a short .txt file and assign each line to variables containing the line number in the variable name.
File example.txt looks like this:
Line A
Line B
When I run the following code:
i=1
while read line; do
eval line$i="$line"
echo $line
((i=i+1))
done < example.txt
What I would expect during execution is:
Line A
Line B
and afterwards being able to call
$ echo $line1
Line A
$ echo $line2
Line B
However, the code above results in the error:
-bash: A: command not found
Any ideas for a fix?
Quote-removal happens twice with eval. Your double-quotes are getting removed before eval even runs. I'm not even going to directly answer that part, because there are better ways to do this:
readarray line < example.txt # bash 4
echo "${line[0]}"
Or, to do exactly what you were doing, with a different variable for each line:
i=1
while read line$((i++)); do
:
done < example.txt
Also check out printf -v varname "%s" value for a better / safer way to assign by reference.
Check out the bash-completion code if you want to see some complicated call-by-reference bash shenanigans.
Addressing your comment: if you want to process lines as they come in, but still save previous lines, I'd go with this construct:
lines=()
while read l;do
lines+=( "$l" )
echo "my line is $l"
done < "$infile"
This way you don't have to jump through any syntactic hoops to access the current line (vs. having to declare a reference-variable to line$i, or something.)
Bash arrays are really handy, because you can access a single element by value, or you can do "${#lines[#]}" to get the line count. Beware that unset lines[4] leaves a gap, rather than renumbering lines[5:infinity]. See the "arrays" section in the bash man page. To find the part of the manual that documents $# expansion, and other stuff, search in the manual for ##. The Parameter Expansion section is the first hit for that in the bash 4.3 man page.
eval line$i="$line" tells bash to evaluate the string "line1=Line A", which attempts to invoke a command named A with the environment variable "line1" set to the value of Line. You probably want to do eval "line$i='$line'"

Passing values of one file into another file as inputs

I have a file that contains following information in a tab separated manner:
abscdfr 2 5678
bgbhjgy 7 8756
ptxfgst 5 6783
lets call this file A and it contains 2000 lines
and I have another file B written in ruby
that takes these values as command line input:
f_id = ARGV[0]
lane = ARGV[1].to_i
sample_id = ARGV[2].to_i
puts " #{f_id}_#{lane}_#{sample_id}.bw"
I execute the file B in ruby by providing the information in file A
./fileB.rb abscdfr 2 5678
I want to know how can I pass the values of file A as input to file B in a recursive manner.
If it was one value it was easy but I am confused with three values.
Kindly help me in writing a wrapper around these two file either in bash or ruby.
Thank you
The following command will do the job in bash:
while read line; do ./fileB.rb $line; done < fileA
This reads each lines into line. Then it runs ./fileB.rb $line for each line. $line gets replaced before the command line is evaluated, thus each word in every line is passed as its own argument, it is important that there is no quotation like "$line". read reads from STDIN and would usually wait for user input, but with < fileA the content of fileA is redirected to STDIN so that read takes its input from there.
You could use a little bash script to loop through each line in the file and output the contents as arguments to another script.
while read line; do
eval "./fileB.rb $line"
done < fileA
This will evaluate the line in the quotes as if you typed it into the shall yourself.
Also you can use one liner ruby :
ruby -ne 'system( "./fileB.rb #{$_}" )' < fileA
Explanation :
-e Which allow us to specifies script from command-line
-n The other useful flags are -n (somewhat like sed -n or awk) , the flag tell ruby to read input or input file line by line like while loop.
$_ Default ruby save current line stored in $_ variable

Error in bash script while reading a file

The following is a script I wrote to run an executable ./runnable on argument/input file input.
It takes standard input from another file called final_file and outputs it to a file called outfile. There are 91 lines in final_file (i.e., 91 different standard space delimited inputs) and therefore the bash script should call the ./runnable input 91 times.
But, I am not sure why it is calling it only one time. Any suggestions on what's going on wrong?
#!/bin/bash
OUTFILE=outfile
(
a=0
while read line
do
./runnable input
echo "This is line number: $a"
a='expr $a+ 1'
done<final_file
) >$OUTFILE
To clarify, the final_file looks like
_ _DATA_ _
2,9,2,9,10,0,38
2,9,2,10,11,0,0
2,9,2,11,12,0,0
2,9,2,12,13,0,0
2,9,2,13,0,1,4
2,9,2,13,3,2,2
and so on. One line, at a time, is the standard input. Number of lines in final_file correspond to number of times the standard input is given. So in the above case, the script should run six times as there are six lines.
I'll hazard that ./runnable seeks all the way through stdin. With no input left to read, the while loop ends after one iteration.
Reasoning: your example Works For Me (TM), substituting a file I happen to have (/etc/services) for final_file and commenting out the line that invokes ./runnable.
On the other hand, if I replace the ./runnable invocation with a one-liner that simply seeks and discards standard input (e.g., cat - > /dev/null or perl -ne 1), I get the behavior you describe.
(Note that you want backticks or $() around the call to expr.)
Run your shell script with the -x
option for some debugging output.
Add echo $line after your while read line; do
Note that while read line; do echo $line; done does not read space separated input, it reads line separated input.

Resources