I am doing something interesting with bash
I wrote script below:
#!/bin/bash
while :
do
if [ -s /tmp/file.txt ]; then
for line in $(cat /tmp/file.txt)
do
echo $line
#May be some commands here
done
fi
done
and the content of my file.txt is:
1 True
2 Flase
How can I say the script if command cat /tmp/file.txt is finished (I mean all lines are read) and also echo $line and other commands are finished then break the infinitive while : loop?
Thank you
Use break.
#!/bin/bash
while :
do
if [ -s /tmp/file.txt ]; then
for line in $(cat /tmp/file.txt)
do
echo $line
#May be some commands here
done
break
fi
done
Although it would be simpler and more proper with:
#!/bin/bash
for (( ;; )); do
if [[ -s /tmp/file.txt ]]; then
# Never use `for X in $()` when reading output/input. Using word splitting
# method for it could be a bad idea in many ways. One is it's dependent with
# IFS. Second is that glob patterns like '*' could be expanded and you'd
# produce filenames instead.
while read line; do
# Place variables between quotes or else it would be subject to Word
# Splitting and unexpected output format could be made.
echo "$line"
done < /tmp/file.txt
break
fi
done
On another note, do you really need the outer loop? This time you don't need to use break.
#!/bin/bash
if [[ -s /tmp/file.txt ]]; then
while read line; do
echo "$line"
done < /tmp/file.txt
fi
Related
I have a script that must be able to accept both by files and stdin on the first argument. Then if more or less than 1 arguments, reject them
The goal that I'm trying to accomplish is able to accpet using this format
./myscript myfile
AND
./myscript < myfile
What I have so far is
if [ "$#" -eq 1 ]; then #check argument
if [ -t 0 ]; then #check whether input from keyboard (read from github)
VAR=${1:-/dev/stdin} #get value to VAR
#then do stuff here!!
else #if not input from keyboard
VAR=$1
if [ ! -f "$VAR" ]; then #check whether file readable
echo "ERROR!"
else
#do stuff heree!!!
fi
fi
fi
The PROBLEM is when I tried to say
./myscript < myfile
it prints
ERROR!
I dont know whether this is the correct way to do this, I really appreciate for suggestion or the correct code for my problem. Thank you
#!/bin/bash
# if nothing passed in command line pass "/dev/stdin" to myself
# so all below code can be made branch-free
[[ ${#} -gt 0 ]] || set -- /dev/stdin
# loop through the command line arguments, treating them as file names
for f in "$#"; do
echo $f
[[ -r $f ]] && while read line; do echo 'echo:' $line; done < $f
done
Examples:
$ args.sh < input.txt
$ args.sh input.txt
$ cat input.txt | args.sh
I would like to split a line into words. I know this can be done with this
For word in $line; do echo $word; done
But I want to make group of 3-3 words. So my question is, how can I split a line in group of 3-3 words ?
For example
Input : I am writing this line for testing the code.
Output :
I am writing
this line for
testing the code.
Read the words three at a time. Set the line being read from to the remainder:
while read -r remainder
do
while [[ -n $remainder ]]
do
read -r a b c remainder <<< "$remainder"
echo "$a $b $c"
done
done < inputfile
What about paste command
for word in $line; do echo $word; done | paste - - -
for word in $line; do echo $word; done | paste -d" " - - -
Easy regex exercise.
sed -e "s/\([^\ ]*\ [^\ ]*\ [^\ ]*\)\ /\1\\`echo -e '\n\r'`/g"
The only tricky part was getting the new line in the sed, as there isn't a standard for that.
$ echo "I am writing this line for testing the code."|sed -e "s/\([^\ ]*\ [^\ ]*\ [^\ ]*\)\ /\1\\`echo -e '\n\r'`/g"
I am writing
this line for
testing the code.
You're welcome.
Just use set to set your input as positional arguments, and process them in groups of three. That way you don't need anything fancy or bash-specific:
line="I am writing this line for testing the code."
set junk $line
shift
while [ $# -ge 3 ]; do
echo "Three words: $1 $2 $3"
shift 3
done
As a start you can use this, which reads every word into an array
#!/bin/bash
total=0
while read
do
for word in $REPLY
do
A[$total]=$word
total=$(($total+1))
done
done < input.txt
for i in "${A[#]}"
do
echo $i
done
Next step is to use seq or similar to loop through the array and print it in groups of three.
There's a non-generic straight forward solution:
#!/bin/bash
path_to_file=$1
while read line
do
counter=1;
for word in $line
do
echo -n $word" ";
if (($counter % 3 == 0))
then
echo "";
fi
let counter=counter+1;
done
done < ${path_to_file}
Save that in a script, give it a name (test.sh for example) and set it to execution mode. Than if your text is saved in "myfile.txt" call it like this:
test.sh myfile.txt
Here's an example of possible solution.
#!/bin/bash
line="I am writing this line for testing the code."
i=0
for word in $line; do
((++i))
if [[ $i -eq 3 ]]; then
i=0
echo "$word"
else
echo -ne "$word "
fi
done
So I have this block of code. Basically, I'm taking file $i, checking if it's got content or not, checking if I can read it, if I can open it, grab the first line and see if it's a bash file. When I run this every time on a non-empty file, it was registers as true and echo's bash.
## File is empty or not
if [[ -s $i ]]
then
## Can we read the file
if [[ -r $i ]]
then
## File has content
if [[ $(head -n 1 $i) = "#! /bin/bash" ]]
then
echo -n " bash"
fi
fi
else
## file does not have content
echo -n " empty"
fi
This is what does the check of if it's bash:
if [[ $(head -n 1 $i) = "#! /bin/bash" ]]
Replace [[ with [ and enclose $(head -n 1 $i) in quotes.
[[ is itself an operator that tests its contents.
I've the following script.
for args
do
while read line; do
# do something
done <"$args"
done
If the script is started with a list of filenames, it should read out each file line by line.
Now I'm looking for a way the read from stdin when script is started without a list of filenames, but I doesn't want to duplicate the while loop.
Any ideas?
Quick answer:
[[ -z $1 ]] && defaultout=/dev/stdin
for f in "$#" $defaultout; do
while read line; do
# do something
done < "$f"
done
Drawback: parameters are not parsed
Second attempt:
[[ -z $1 ]] && defaultout=/dev/stdin
for f in $# $defaultout; do
if [[ -f $f ]]; then
while read line; do
# do something
done < "$f"
fi
done
Drawback: Filenames with spaces will be parsed into two words.
You could try:
args="$*"
if [ "$args" = "" ]; then
args=/dev/stdin;
fi
for arg in $args; do
while read -r line; do
# do something
done < "$arg";
done
The following should do what you want:
cat "$#" | while read line; do
# something
done
I have a shell script like this:
cat file | while read line
do
# run some commands using $line
done
Now I need to check if the line contains any non-whitespace character ([\n\t ]), and if not, skip it.
How can I do this?
Since read reads whitespace-delimited fields by default, a line containing only whitespace should result in the empty string being assigned to the variable, so you should be able to skip empty lines with just:
[ -z "$line" ] && continue
try this
while read line;
do
if [ "$line" != "" ]; then
# Do something here
fi
done < $SOURCE_FILE
bash:
if [[ ! $line =~ [^[:space:]] ]] ; then
continue
fi
And use done < file instead of cat file | while, unless you know why you'd use the latter.
cat i useless in this case if you are using while read loop. I am not sure if you meant you want to skip lines that is empty or if you want to skip lines that also contain at least a white space.
i=0
while read -r line
do
((i++)) # or $(echo $i+1|bc) with sh
case "$line" in
"") echo "blank line at line: $i ";;
*" "*) echo "line with blanks at $i";;
*[[:blank:]]*) echo "line with blanks at $i";;
esac
done <"file"
if ! grep -q '[^[:space:]]' ; then
continue
fi
blank=`tail -1 <file-location>`
if [ -z "$blank" ]
then
echo "end of the line is the blank line"
else
echo "their is something in last line"
fi
awk 'NF' file | while read line
do
# run some commands using $line
done
stole this answer to a similar question:
Delete empty lines using sed