Concatenate Unknown Multiple Text Files While Inserting a Divider After the Contents of Each File Shell Script - linux

For this problem in shell script, I need to accept an unknown multiple number of texts files and concatenate them. But I need to put a divider such as (-----) after the contents of each file. There also needs to be an exit code for the script where if it succeeds then it prints out the correct output. But if it fails it needs to print out this help message, "Usage..." (It's long and the help message itself isn't that important.)
I already figured out how to accept multiple number of text files to concatenate them but the problem is the divider really.
I also figured out how to show the help message using cat but the problem is that I don't know what conditions I need to put for the if and else statement because the conditions itself isn't working as intended.
For example, if it can find txt files and print the output it works as intended, however if there is no txt file it doesn't print the help message.
I am pretty sure it has to be some kind of loop statement for the divider to work but the problem is I have no idea how to do it. Not even sure of the conditions.
# Don't know what condition to put for the if here
if [ $? -eq 0 ]
then
# Don't know what condition to put for the for loop here
for j in "$?"
do
cat *.txt
echo "-----"
done
exit 0
else
cat <<< "Usage..." >&2
exit 1
fi
Let's say we have 2 text files.
one.txt has:
Hello
two.txt has:
Happy
Then if the code runs as intended the output should be:
Hello
-----
Happy
-----

Something amongst those lines ?
#!/bin/bash
for param in $#; do
if [[ -f $param ]]; then
cat $param
echo "-----"
else
echo "wrong parameter passed! usage:..."
exit 1
fi
done
exit 0
To explain quickly :
for param in $#; do
Iterate over every parameter passed to the script
[[ -f $param ]]
This checks whether the parameter is an existing file

Related

How do i append text to a file after checking if the text is not already available in the file?

I am new to scripting with bash, and I am trying to make a user friendly script to download videos using youtube-dl.
After getting the available streams from a video link, i want to write to a file the available resolutions for the given video. This is my code:
#!/bin/bash
youtube-dl -F https://www.youtube.com/watch?v=05X0RRmUtE0 > info.txt
filename='info.txt'
echo Start
cat > res.txt
echo "" > res.txt
while read p; do
for i in '240p' '360p' '480p' '720p' '1080p' '2160p'; do
while read q; do
if [[$i == $q]]; then
break
else
echo $i >> res.txt
fi
done < res.txt
done
done < $filename
With the current script, '240p' endlessly gets appended to 'res.txt'. I am not able to figure out where i am going wrong
UPDATE
i have edited the code as follows:
while read p ; do
for i in '240p' '360p' '480p' '720p' '1080p' '2160p' ; do
if [[ "$p" == *"$i"* ]]; then
while read q; do
if [[ "$q" == "$i" ]]; then
break
else
echo $i >> res.txt
fi
done < res.txt
fi
done
done < info.txt
but i still do not get the ouput I want
also, for those who do not understand what the script is supposed to do:
1) input a link and extract the available streams to a text file ( youtube-dl -F https://www.youtube.com/watch?v=05X0RRmUtE0 > info.txt)
2) append recurring resolutions from the streams in info.txt to another file res.txt only once
I'll ignore the outer loop (read p ... < $filename) since it does not impact the issue you're asking about, but you should probably check why it's there because it doesn't do anything right now.
The first inner loop (for i in '240p' '360p' ...) loops over that list, and performs the second inner loop for every value.
The second inner loop (read q ... < res.txt) reads res.txt, and compares every line to the value from the first inner loop. If the values match, the second inner loop stops, and the first inner loop continues with the next value, restarting the second inner loop at the beginning of the file.
If the values do not match, you add '240p' to the res.txt file. If it never finds a matching value, it will keep going endlessly, because it does not simply use the original contents of the file, it also reads lines added to this file during the process.
This means that, if the res.txt file does not contain one of the values of the first inner loop, it will get stuck at that value, and keep adding '240p' endlessly, except if the original file does not contain '240p', in which case it will read the entire file, adding a '240p' line for every value it already contains, then break when it encounters the first '240p' it added.
The only way this code will not get stuck is if res.txt contains all of the values from the first inner loop, or all except '240p' (which it will add, then finish).
EDIT:
As for a solution, I'd throw out the entire inner while loop, replacing it with grep:
#!/bin/bash
youtube-dl -F https://www.youtube.com/watch?v=05X0RRmUtE0 > info.txt
echo Start
rm res.txt >/dev/null 2>&1 # remove res.txt if it exists, don't care about warnings if it doesn't exist
touch res.txt # create empty file
while read p ; do
for i in '240p' '360p' '480p' '720p' '1080p' '2160p' ; do
if [[ "$p" =~ "$i" ]]; then # regex matching ensures foo480pbar matches '480p'
if ! grep "$p" res.txt >/dev/null 2>&1; then # if grep fails, res.txt does not contain $p yet
echo $i >> res.txt
fi
fi
done
done < info.txt
You could even throw out the for loop and put all the resolutions in one regex match, and combine both if statements into one, but this way it's easier to read what's going on, so I'll leave that as an exercise to the reader 😉

Shell Script working with multiple files [duplicate]

This question already has answers here:
How to iterate over arguments in a Bash script
(9 answers)
Closed 5 years ago.
I have this code below:
#!/bin/bash
filename=$1
file_extension=$( echo $1 | cut -d. -f2 )
directory=${filename%.*}
if [[ -z $filename ]]; then
echo "You forgot to include the file name, like this:"
echo "./convert-pdf.sh my_document.pdf"
else
if [[ $file_extension = 'pdf' ]]; then
[[ ! -d $directory ]] && mkdir $directory
convert $filename -density 300 $directory/page_%04d.jpg
else
echo "ERROR! You must use ONLY PDF files!"
fi
fi
And it is working perfectly well!
I would like to create a script which I can do something like this: ./script.sh *.pdf
How can I do it? Using asterisk.
Thank you for your time!
Firstly realize that the shell will expand *.pdf to a list of arguments. This means that your shell script will never ever see the *. Instead it will get a list of arguments.
You can use a construction like the following:
#!/bin/bash
function convert() {
local filename=$1
# do your thing here
}
if (( $# < 1 )); then
# give your error message about missing arguments
fi
while (( $# > 0 )); do
convert "$1"
shift
done
What this does is first wrap your functionality in a function called convert. Then for the main code it first checks the number of arguments passed to the script, if this is less than 1 (i.e. none) you give the error that a filename should be passed. Then you go into a while loop which is executed as long as there are arguments remaining. The first argument you pass to the convert function which does what your script already does. Then the shift operation is performed, what this does is it throws away the first argument and then shifts all the remaining arguments "left" by one place, that is what was $2 now is $1, what was $3 now is $2, etc. By doing this in the while loop until the argument list is empty you go through all the arguments.
By the way, your initial assignments have a few issues:
you can't assume that the filename has an extension, your code could match a dot in some directory path instead.
your directory assignment seems to be splitting on . instead of /
your directory assignment will contain the filename if no absolute or relative path was given, i.e. only a bare filename
...
I think you should spend a bit more time on robustness
Wrap your code in a loop. That is, instead of:
filename=$1
: code goes here
use:
for filename in "$#"; do
: put your code here
done

Bash shell string contains print full word

the code:
READLINE=$(sudo /bin/cat < "file.txt");
IFS=$';'
for i in $READLINE;
do
txt=$txt" ""$i";
done
unset IFS
for x in $arr
do
if [[ $txt == *$x* ]] ; then
echo "$x is in the txt file";
output file.txt
telecomtest;yess;
output for loop:
telecomtest is in the txt file
tel is in the txt file
I want only print the full word not a part of it.
that means only print telecomtest and not tel.
How can i make this work?
Your $arr appears to contain both telecomtest and tel. Since you don't show us how $arr is assigned, everything else is speculation. Did you delete too much of the script to post it here? There's also a fi missing to make this valid shell grammar.
Please provide a minimal runnable script for us, including some stripped down contents of file.txt so we are able to reproduce the problem. Often this will uncover the problem right away.

Scripts in sed - linux

I got the concept of bash, now, I found a site full of riddles for practising bash. I solve a couple of scripts (you should mention what do they do, what they are missing or so, depends on the question) and I bumped this script:
random_var="$(echo $1 | sed -e 's/[^[:alnum:]]//g')"
Correct me if I'm wrong about my basic assumptions on the following code:
$1 is the second argument that the script got (when the first is the script name)
There is a pipeline between the second argument and the sed script that removes all alpha numerics and... according to what I understand, this script can be "broken" by using a delimiter such as [/\]^$ and so ?
Now, there comes the difficulty (well, for me), the program gets an input from the user and, when the following script I just mention is found at a function returning true if the input is different than the result. I have no idea what is happening here, can someone enlighten me?
#!/bin/sh
func()
{
somevar="$(echo $1 | sed -e 's/[^[:alnum:]]//g')"
if [ "$somevar" != "$input" ] ; then
return 1
else
return 0
fi
}
# Sample usage of this function in a script
echo -n "Enter input: "
read input
if ! func "$input" ; then
echo "HELL NO"
exit 1
else
echo "YES!"
fi
exit 0
The script tests a string to see whether it contains any non-alphanumeric characters.
As Avinash has mentioned in the comments, the sed command removes all non-alphanumeric characters. Within the function, $input has the same value as it does in the calling scope, which is also the same as the first argument, $1. This is perhaps a little bit confusing...
If $somevar is different to $input (=$1), then this means that sed has changed the string in some way. Therefore, the string must contain at least one non-alphanumeric character.
If the function returns 1 (there were some non-alphanumeric characters in the input), then ! func is false, so the else branch will be executed and the script will return with an exit code of 0 (success). Otherwise, the script will return a non-zero exit code, indicating a failure.

Attempting to pass two arguments to a called script for a pattern search

I'm having trouble getting a script to do what I want.
I have a script that will search a file for a pattern and print the line numbers and instances of that pattern.
I want to know how to make it print the file name first before it prints the lines found
I also want to know how to write a new script that will call this one and pass two arguments to it.
The first argument being the pattern for grep and the second the location.
If the location is a directory, it will loop and search the pattern on all files in the directory using the script.
#!/bin/bash
if [[ $# -ne 2 ]]
then
echo "error: must provide 2 arguments."
exit -1
fi
if [[ ! -e $2 ]];
then
echo "error: second argument must be a file."
exit -2
fi
echo "------ File =" $2 "------"
grep -ne "$1" "$2"
This is the script i'm using that I need the new one to call. I just got a lot of help from asking a similar question but i'm still kind of lost. I know that I can use the -d command to test for the directory and then use 'for' to loop the command, but exactly how isn't panning out for me.
I think you just want to add the -H option to grep:
-H, --with-filename
Print the file name for each match. This is the default when there is more than one file to search.
grep has an option -r which can help you avoid testing for second argument being a directory and using for loop to iterate all files of that directory.
From the man page:
-R, -r, --recursive
Recursively search subdirectories listed.
It will also print the filename.
Test:
On one file:
[JS웃:~/Temp]$ grep -r '5' t
t:5 10 15
t:10 15 20
On a directory:
[JS웃:~/Temp]$ grep -r '5' perl/
perl//hello.pl:my $age=65;
perl//practice.pl:use v5.10;
perl//practice.pl:#array = (1,2,3,4,5);
perl//temp/person5.pm:#person5.pm
perl//temp/person9.pm: my #date = (localtime)[3,4,5];
perl//text.file:This is line 5

Resources