Im writing a data formating bash script. The script reads the file name which is input by the user in the terminal. Of course, when no file under that name is found in that directory, the program ends with a stderr output. I am now trying to implement a (while) loop which recursively asks for user input until a matching file is found and then goes on to executing my data formating commands. Would appreciate some help :)
I am now trying to implement a (while) loop which recursively
I would advice against using recursion for this. Just make a simple loop. It could look like this:
while true; do
IFS= read -rp 'File: ' file
if [[ -e $file ]]; then
break;
fi
echo "$file doesn't exist, try again"
done
# work with $file here
Related
How do I create a Bash script that takes a file name as input? Then, if that file exists, it should print "File exists"; if not, print "File does not exist".
For example, if I ran ./do-i-exist.sh ./do-i-exist.sh, the output should be only 'File exists'
file="$1"
read answer
if [ $file != -$2 ]
then
echo "File exists"
else
echo "File does not exist"
fi
This is what I'm working with but is not working for me, whenever I add an extension like .sh, .txt or something similar it won't find the file.
The test if a file exists can be done like this
if [ -f "$file" ]
then
This tests for a regular file, not for other kinds of files like a directory.
This is how you can do it. Pass the name of the file while like ./do-i-exist.sh file_path.
if [ -f "$1" ]
then
echo "File Exists"
else
echo "File does not exist"
fi
First of all, I want to thank anyone and everyone who tried to help. After 3 hard working days, I found the answer, here it is:
#!/bin/bash
file="$#"
if [ -f $file ]
then
echo "File exists"
else
echo "File does not exist"
fi
Using this table:
Variable Name
Description
$0
The name of the Bash script
$1 - $9
The first 9 arguments to the Bash script
$#
Number of arguments passed to the Bash script
$#
All arguments passed to the Bash script
$?
The exit status of the most recently run process
$$
The process ID of the current script
$USER
The username of the user running the script
$HOSTNAME
The hostname of the machine
$RANDOM
A random number
$LINENO
The current line number in the script
I and other users were focused on using $1 from my understanding this refers to the first argument passed to the script but for some reason, it wasn't working since it needed to pass more inputs.
As from my previous comments I didn't have control over the input. The input was hidden in a locked file, and I needed to feed my script to it.
From what we know $0 is only used to check for the file names, $1 to get the first statement and $# will just take anything(I guess).
I know absolutely nothing about bash and it was the first time ever using it, which is why it took me 3 days to solve this puzzle. This was part of a CTF and just like me, many others may struggle in the future to understand or know how to make a script that will just adapt to a series of inputs from a second script.
This is how it was supported to work:
I was given access to a very restricted server and on this server, I was given the encrypted-file.sh file. This file was supposed to be fed to /path/to/myfile.sh then encrypted-file.sh would execute a second command to open a third locked file hiding a flag on it.
This only works with the right bash file using the right variables on it for encrypted-file.sh to run without errors, which is what I accomplished here.
I used a while loop because it made sense in my case because I really needed a file for the script to work.
restore_file="$1"
while [ ! -f "$restore_file" ]
do
echo "File not found: $restore_file"
echo "Please provide a valid file:"
read restore_file
done
As written above, $1 is the first argument given to the script. In this case if no argument is given or that is not a file, it will prompt again.
By the way, use -d instead of -f to check for a directory.
I am trying to create a basic MapReduce function in bash (I am very new at it). I have two scripts at the moment, job_master.sh and map_function.sh. I am trying to run the map function from the job master to cut from a data file, and if it doesn't exist send it to key, but if it does send it to the file of that name. Nothing is happening when I run the job_master script or the map_function script on its own with a file as an argument. It was working before I added the if statements into the map_function.
I have included both codes below if anyone is able to spot why they are not running. I tried including echo statements to test and it is not entering the loop in the job_master script, or doing anything at all in the map_function script.
MAP_FUNCTION
#!/bin/bash
while IFS="," read -r date prod remainder; do
if [ ! -e "$prod" ];
then
echo $prod >> keys
else
echo $prod >> $prod
fi
done
JOB_MASTER
#!/bin/bash
files=$(ls | egrep 'sales_a*')
for elem in $files ; do
./map_function.sh $elem
done
The map function script is waiting for input. Look at this:
while IFS="," read -r date prod remainder; do
# ...
done
Where will read get its input? It's waiting on stdin, but you're not passing it anything.
On the other hand, I see that you're calling the map function script like this:
./map_function.sh $elem
That is, you are passing a command line argument. But the map function script doesn't use that argument. It seems you want to redirect the content of $elem to the stdin of the script.
Write like this:
for elem in sales_a*; do
./map_function.sh < "$elem"
done
This also fixes some other issues you had in the script. files=$(ls | grep 'sales_a*') looks buggy, and it's an inappropriate way to iterate over files.
I'm very new to bash scripting and here's what i'm trying to do:
1 - Read a file - this file is a list of names
2 - Ask user if they want to delete {name}
3 - If user enters y, proceed
This is what my script looks so far:
while IFS= read -r repo
do
read -p "Do you want to delete $repo" ip
echo $ip
if [ "$ip" == "y" ]
then
#do something
fi
done < "$filename"
The read -p line does not wait for a user prompt. I sort of understood what/where the problem is and I was trying to resolve it by reading up on this link - https://bash.cyberciti.biz/guide/Reads_from_the_file_descriptor_(fd)
But somehow I was unable to resolve this issue. What am I doing wrong? Please help!
Use a different file descriptor for the named file. You know where that data is coming from; you don't know where standard input might be redirected from, so leave it alone.
while IFS= read -r -u 3 repo # Read from file descriptor 3
do
read -p "Do you want to delete $repo" ip # Read from whatever standard input happens to be
echo "$ip"
if [ "$ip" = "y" ]
then
#do something
fi
done 3< "$filename" # Supply $filename on file descriptor 3
-u is bash-specific, but I note you are already using another bash-specific feature, the -p option to read. The POSIX way to read from something other than standard input is IFS= read -r repo <&3 (which says, copy file descriptor 3 onto standard input for this command).
See this question:
Does bash support doing a read nested within a read loop?
Essentially, you're reading in the file through standard input which is the same input stream as when you type, so when you prompt the user, the script is treating the file's input as the user's input. If you instead read in the the file on another input stream then it wouldn't override.
What I have to to is edit a script given to me that will check if the user has write permission for a file named journal-file in the user's home directory. The script should take appropriate actions if journal-file exists and the user does not have write permission to the file.
Here is what I have written so far:
if [ -w $HOME/journal-file ]
then
file=$HOME/journal-file
date >> file
echo -n "Enter name of person or group: "
read name
echo "$name" >> $file
echo >> $file
cat >> $file
echo "--------------------------------" >> $file
echo >> $file
exit 1
else
echo "You do not have write permission."
exit 1
fi
When I run the script it prompt me to input the name of the person/group, but after I press enter nothing happens. It just sits there allowing me to continue inputting stuff and doesn't continue past that part. Why is it doing this?
The statement:
cat >>$file
will read from standard input and write to the file. That means it will wait until you indicate end of file with something like CTRL-D. It's really no different from just typing cat at a command line and seeing that nothing happens until you enter something and it waits until you indicate end of file.
If you're trying to append another file to the output file, you need to specify its name, such as cat $HOME/myfile.txt >>$file.
If you're trying to get a blank line in there, use echo rather than cat, such as echo >>$file.
You also have a couple of other problems, the first being:
date >> file
since that will try to create a file called file (in your working directory). Use $file instead.
The second is the exit code of 1 in the case where what you're trying to do has succeeded. That may not be a problem now but someone using this at a later date may wonder why it seems to indicate failure always.
To be honest, I'm not really a big fan of the if ... then return else ... construct. I prefer fail-fast with less indentation and better grouping of output redirection, such as:
file=${HOME}/journal-file
if [[ ! -w ${file} ]] ; then
echo "You do not have write permission."
exit 1
fi
echo -n "Enter name of person or group: "
read name
(
date
echo "$name"
echo
echo "--------------------------------"
echo
) >>${file}
I believe that's far more readable and maintainable.
It's this line
cat >> $file
cat is concatenating input from standard input (ie whatever you type) to $file
I think the part
cat >> $file
copies everything from stdin to the file. Maybe if you hid Ctrl+D (end of file) the script can continue.
1) You better check first whether the file exists or not:
[[ -e $HOME/journal-file ]] || \
{ echo "$HOME/journal-file does not exist"; exit 1 }
2) You gotta change "cat >> $file" for whatever you want to do with the file. This is the command that is blocking the execution of the script.
How come this while loop only executes once even though there are 2 entries (lines) in the .csv file?
And how come when no files/directories match the expression, the string 'ls: /root/heapdump: No such file or directory' is output to console also?
#!/bin/bash
INPUT=targets.csv
[ ! -f $INPUT ] && { echo "$INPUT file not found"; exit 99; }
while IFS=, read target user password path
do
result=$(sshpass -p "$password" ssh "$user"#"$target" ls "$path"*heapdump*)
if [ $? -ne 0 ]
then
echo "No Heap dumps detected."
else
echo "Found a Heap dump! Possible OOM issue detected"
fi
done < $INPUT
.csv contents ..
rob#laptop:~/scripts$ cat targets.csv
server.com,root,passw0rd,/root/
server.com,root,passw0rd,/root/
Output ..
rob#laptop:~/scripts$ ./checkForHeapdump.sh
ls: /root/*heapdump*: No such file or directory
No Heap dumps detected.
ssh is reading from stdin, so it's slurping up the rest of the CSV file during the first iteration. Change to:
result=$(sshpass -p "$password" ssh -n "$user"#"$target" ls "$path"*heapdump*)
The -n option redirects ssh stdin to /dev/null.
Regarding the error message, that's the normal error output when you do:
ls filename-or-wildcard
and there are no matches. If you don't want to see that, redirect stderr:
result=$(sshpass -p "$password" ssh -n "$user"#"$target" ls "$path"*heapdump* 2>/dev/null)
I am guessing the final line of your CSV file is not terminated with a carriage return.
When this happens read will populate the variables but without the carriage return to terminate the line it will instead reach the end of file and so the loop will not execute.
If you cannot confirm the final line will be terminated with a carriage return, and are unable to edit the file by putting echo >> targets.csv at the start of the script to add one, then you can check the variables after the loop to act on them. To do this I would clear one of the variables at the end of the loop so then you can simply test whether it is non-zero length to know there is one final unprocessed line.