I have a bash script that works as. A user inputs a filename and then the script searches through the directories and when its found it copies to another location so that the file gets re-processed again. The script works well on bash script. My problem is that I'm not sure how to process that with PHP so that the user does it through the website or html. Here is my bash script - user_input.sh
#!/bin/bash
echo -n "Enter required file: "
read file_name
search=`find /var/log -type f -name "$file_name*"`
if [[ $? == 0 ]]
then
echo "$search will be processed soon"
cp $search /root
echo "$search copied for re-processing. Please check after about 5 minutes."
else
echo "Required file $file_name not found"
fi
Here is my PHP script, user_in.php:
<?php
exec("user_input.sh");
?>
Related
I want to remove the extension of specific files with a given extension.
So for instance, in a directory foobar, we have foo.txt, bar.txt foobar.jpg.
Additionally, the extension that I've put in to be removed is txt
After calling the program, my output should be foo bar foobar.jpg
Here is my code so far:
#!/bin/bash
echo "Enter an extension"
read extension
echo "Enter a directory"
read directory
for file in "$directory"/*; do //
if [[ $file == *.txt ]]
then
echo "${file%.*}"
else
echo "$file"
fi
done
However when I run this on a given directory, nothing shows up.
I'm assuming that there is a problem with how I referred to the directory ( in the line where I placed a //) and I've tried to research on how to solve it but to no avail.
What am I doing wrong?
If files do exist in a valid directory you've entered then they should show up — with one exception. If you are using ~/ (shorthand home directory) then it will be treated as plain text in your for loop. The read variable should be substituted into another variable so the for loop can treat it as a directory (absolute paths should work normally as well).
#!/bin/bash
echo "Enter an extension"
read -r extension
echo "Enter a directory"
read -r directory
dir="${directory/#\~/$HOME}"
for file in "$dir"/*; do
if [[ $file == *."$extension" ]]
then
echo "${file%.*}"
else
echo "$file"
fi
done
You can simplify your for-loop:
for file in "$directory"/*; do
echo "${f%.$extension}";
done
The % instructions removes only matching characters. If nothing matches, the original string (here f) is returned.
When you write bash scripts it's more common to pass arguments to your script via command line arguments rather than by reading it from standard input via read program.
Passing arguments via command line:
#!/bin/bash
# $# - a bash variable which holds a number of arguments passed
# to script via command line arguments
# $0 holds the name of the script
if [[ $# -ne 2 ]]; then # checks if exactly 2 arguments were passed to script
echo "Usage: $0 EXTENSION DIRECTORY"
exit -1;
fi
echo $1; # first argument passed to script
echo $2; # second arugment passed to script
This approach is more efficient because a subprocess is spawn for read command to run and there is no subprocess spawn for reading command line arguments.
There is no need to manually loop through directory, you can use find command to find all files with given extension within given directory.
find /path/to/my/dir -name '*.txt'
find $DIRECTORY -name "*.$EXTENSION"
# note that single quotes in this context would prevent $EXTENSION
# variable to be resolved, so double quotes are used " "
# find searches for files inside $DIRECTORY and searches for files
# matching pattern '*.$EXTENSION'
Note that to avoid bash filename expansion sometimes it is required to wrap actual pattern in single quotes ' ' or double quotes " ". See Bash Filename Expansion
So now your script can look like this:
#!/bin/bash
if [[ $# -ne 2 ]]; then
echo "Usage: $0 EXTENSION DIRECTORY"
exit -1;
fi
$EXTENSION = $1 # for better readability
$DIRECTORY = $2
for file in `find $DIRECTORY -name "*.$EXTENSION"`; do
mv $file ${file%.$EXTENSION}
done
Construct ${file%.$EXTENSION} is called Shell Parameter Expansion it searches for occurrence of .$EXTENSION inside file variable and deletes it.
Notice that in the script it is easy to pass extension as directory and vice versa.
We can check if second argument is in fact directory, we can use following construction:
if ! [[ -d $DIRECTORY ]]; then
echo $DIRECTORY is not a dir
exit -1
fi
This way we can exit from the script earlier with more readable error.
To sum up entire script could look like this:
#!/bin/bash
if [[ $# -ne 2 ]]; then
echo "Usage: $0 EXTENSION DIRECTORY"
exit -1;
fi
EXTENSION=$1 # for better readability
DIRECTORY=$2
if ! [[ -d $DIRECTORY ]]; then
echo $DIRECTORY is not a directory.
exit -1
fi
for file in `find $DIRECTORY -name "*.$EXTENSION"`; do
mv $file ${file%.$EXTENSION}
done
Example usage:
$ ./my-script.sh txt /path/to/directory/with/files
I want to use the result of ls command in a loop to check if for example the first line is a directory, second etc.
For example I have this folder that contains one directory the script should display:
18_05_2018 is directory
enter image description here
Create a file named is_file_or_directory.sh containing:
cd "$1" || echo "Please specify a path" && exit
for i in *; do
if [[ -d $i ]]; then
echo "$i is a directory"
elif [[ -f $i ]]; then
echo "$i is a file"
else
echo "$i is not valid"
exit 1
fi
done
Make that file executable with:
sudo chmod +x is_file_or_directory.sh
Run the script specifying as a parameter the path that you want to analyze:
./is_file_or_directory.sh /root/scripts/
Output:
jeeves ~/scripts/stack # ./is_file_or_dir.sh /root/scripts/
databe.py is a file
is_file_or_dir.sh is a file
mysql_flask.py is a file
test is a directory
Here's a more detailed explanation of what is happening under the hood. The variable $1 is, in Bash, the first parameter sent to the script. In our case it is the path where the script will perform its actions. Then we use the variable $i in the loop.
$i content will be every file / folder name in the path $1. With -d and -f we check if $i is a file or a folder.
I am trying to write a bash/shell script to zip up a specific folder and ignore certain sub-dirs in that folder.
This is the folder I am trying to zip "sync_test5":
My bash script generates an ignore list (based on) and calls the zip function like this:
#!/bin/bash
SYNC_WEB_ROOT_BASE_DIR="/home/www-data/public_html"
SYNC_WEB_ROOT_BACKUP_DIR="sync_test5"
SYNC_WEB_ROOT_IGNORE_DIR="dir_to_ignore dir2_to_ignore"
ignorelist=""
if [ "$SYNC_WEB_ROOT_IGNORE_DIR" != "" ];
then
for ignoredir in $SYNC_WEB_ROOT_IGNORE_DIR
do
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/**\*"
done
fi
FILE="$SYNC_BACKUP_DIR/$DATETIMENOW.website.zip"
cd $SYNC_WEB_ROOT_BASE_DIR;
zip -r $FILE $SYNC_WEB_ROOT_BACKUP_DIR -x $ignorelist >/dev/null
echo "Done"
Now this script runs without error, however it is not ignoring/excluding the dirs I've specified.
So, I had the shell script output the command it tried to run, which was:
zip -r 12-08-2014_072810.website.zip sync_test5 -x sync_test5/dir_to_ignore/**\* sync_test5/dir2_to_ignore/**\*
Now If I run the above command directly in putty like this, it works:
So, why doesn't my shell script exclude working as intended? the command that is being executed is identical (in shell and putty directly).
Because backslash quotings in a variable after word splitting are not evaluated.
If you have a='123\4', echo $a would give
123\4
But if you do it directly like echo 123\4, you'd get
1234
Clearly the arguments you pass with the variable and without the variables are different.
You probably just meant to not quote your argument with backslash:
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/***"
Btw, what actual works is a non-evaluated glob pattern:
zip -r 12-08-2014_072810.website.zip sync_test5 -x 'sync_test5/dir_to_ignore/***' 'sync_test5/dir2_to_ignore/***'
You can verify this with
echo zip -r 12-08-2014_072810.website.zip sync_test5 -x sync_test5/dir_to_ignore/**\* sync_test5/dir2_to_ignore/**\*
And this is my suggestion:
#!/bin/bash
SYNC_WEB_ROOT_BASE_DIR="/home/www-data/public_html"
SYNC_WEB_ROOT_BACKUP_DIR="sync_test5"
SYNC_WEB_ROOT_IGNORE_DIR=("dir_to_ignore" "dir2_to_ignore")
IGNORE_LIST=()
if [[ -n $SYNC_WEB_ROOT_IGNORE_DIR ]]; then
for IGNORE_DIR in "${SYNC_WEB_ROOT_IGNORE_DIR[#]}"; do
IGNORE_LIST+=("$SYNC_WEB_ROOT_BACKUP_DIR/$IGNORE_DIR/***") ## "$SYNC_WEB_ROOT_BACKUP_DIR/$IGNORE_DIR/*" perhaps is enough?
done
fi
FILE="$SYNC_BACKUP_DIR/$DATETIMENOW.website.zip" ## Where is $SYNC_BACKUP_DIR set?
cd "$SYNC_WEB_ROOT_BASE_DIR";
zip -r "$FILE" "$SYNC_WEB_ROOT_BACKUP_DIR" -x "${IGNORE_LIST[#]}" >/dev/null
echo "Done"
This is what I ended up with:
#!/bin/bash
# This script zips a directory, excluding specified files, types and subdirectories.
# while zipping the directory it excludes hidden directories and certain file types
[[ "`/usr/bin/tty`" == "not a tty" ]] && . ~/.bash_profile
DIRECTORY=$(cd `dirname $0` && pwd)
if [[ -z $1 ]]; then
echo "Usage: managed_directory_compressor /your-directory/ zip-file-name"
else
DIRECTORY_TO_COMPRESS=${1%/}
ZIPPED_FILE="$2.zip"
COMPRESS_IGNORE_FILE=("\.git" "*.zip" "*.csv" "*.json" "gulpfile.js" "*.rb" "*.bak" "*.swp" "*.back" "*.merge" "*.txt" "*.sh" "bower_components" "node_modules")
COMPRESS_IGNORE_DIR=("bower_components" "node_modules")
IGNORE_LIST=("*/\.*" "\.* "\/\.*"")
if [[ -n $COMPRESS_IGNORE_FILE ]]; then
for IGNORE_FILES in "${COMPRESS_IGNORE_FILE[#]}"; do
IGNORE_LIST+=("$DIRECTORY_TO_COMPRESS/$IGNORE_FILES/*")
done
for IGNORE_DIR in "${COMPRESS_IGNORE_DIR[#]}"; do
IGNORE_LIST+=("$DIRECTORY_TO_COMPRESS/$IGNORE_DIR/")
done
fi
zip -r "$ZIPPED_FILE" "$DIRECTORY_TO_COMPRESS" -x "${IGNORE_LIST[#]}" # >/dev/null
# echo zip -r "$ZIPPED_FILE" "$DIRECTORY_TO_COMPRESS" -x "${IGNORE_LIST[#]}" # >/dev/null
echo $DIRECTORY_TO_COMPRESS "compressed as" $ZIPPED_FILE.
fi
After a few trial and error, I have managed to fix this problem by changing this line:
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/**\*"
to:
ignorelist="$ignorelist $SYNC_WEB_ROOT_BACKUP_DIR/$ignoredir/***"
Not sure why this worked, but it does :)
I am very new to linux scripting & am trying to set up a simple loop which will:
Ask user for file name
Search a specific directory for the file
If no files are found, ask the user to reinput a file name
If files are found, move on to the next step of the script
This is what I have so far, but it is not looping at all(i.e when no files are found, it is not asking the user to re-enter a file name. )
#!/bin/bash
read -p "Enter file name: " file
find /directory/ -name "$file" -print
while [ "$?" -ne 0 ]; do
read -p "File not found. Please re-enter file name: " file
find /directory/ -name "$file" -print
done
echo "rest of script etc"
Any help is appreciated! :)
The easiest way to do this is probably using globstar (available with bash 4)
#!/bin/bash
shopt -s globstar
while true; do
read -p "Enter file name: " file
for f in /directory/**/"$file"; do
echo "$f"
break 2 # escape both loops
done
echo "'$file' not found, please try again."
done
echo "rest of script etc"
It's also possible to do with find, but slightly annoying, given that you can't use standard UNIX exit statuses:
#!/bin/bash
read -p "Enter file name: " file
found=$(find /directory/ -name "$file" -print -quit)
while [[ -z $found ]]; do
read -p "File not found. Please re-enter file name: " file
found=$(find /directory/ -name "$file" -print -quit)
done
echo "$found"
echo "rest of script etc"
Normally I wouldn't recommend parsing the output of find, but in this case we're only concerned as to whether or not there is any output.
The easiest and most portable way might be this:
# Loop until user inputted a valid file name
while true ; do
# Read input (the POSIX compatible way)
echo -n "Enter file name: "
read file
# Use find to check if the file exists
[ $(find /etc -type f -name "$file" 2>/dev/null | wc -l ) != "0" ] && break
# go to next loop if the file does not exist
done
echo "Ok, go on here"
*Note i edited this so my final functioning code is below
Ok so I'm writing a bash script to backup our mysql database to a directory, delete the oldest backup if 10 exist, and output the results of the backup to a log so I can further create alerts if it fails. Everything works great except the if loop to output the results, thanks again for the help guys code is below!
#! /bin/bash
#THis creates a variable with the date stamp to add to the filename
now=$(date +"%m_%d_%y")
#This moves the bash shell to the directory of the backups
cd /dbbkp/backups/
#Counts the number of files in the direstory with the *.sql extension and deletes the oldest once 10 is reached.
[[ $(ls -ltr *.sql | wc -l) -gt 10 ]] && rm $(ls -ltr *.sql | awk 'NR==1{print $NF}')
#Moves the bash shell to the mysql bin directory to run the backup script
cd /opt/GroupLink/everything_HelpDesk/mysql/bin/
#command to run and dump the mysql db to the directory
./mysqldump -u root -p dbname > /dbbkp/backups/ehdbkp_$now.sql --protocol=socket --socket=/tmp/GLmysql.sock --password=password
#Echo the results to the log file
#Change back to the directory you created the backup in
cd /dbbkp/backups/
#If loop to check if the backup is proper size and if it exists
if find ehdbkp_$now.sql -type f -size +51200c 2>/dev/null | grep -q .; then
echo "The backup has run successfully" >> /var/log/backups
else
echo "The backup was unsuccessful" >> /var/log/backups
fi
Alternatively, you could use stat instead of find.
if [ $(stat -c %s ehdbkp_$now 2>/dev/null || echo 0) -gt 51200 ]; then
echo "The backup has run successfully"
else
echo "The backup was unsuccessful"
fi >> /var/log/backups
Option -c %s tells stat to return the size of file in bytes. This will take care of both the presence of file and size greater than 51200. When the file is missing, stat will err out, thus we redirect error message to /dev/null. The logical or condition || will get executed only when the file is missing thus the comparison will make [ 0 -gt 100 ] false.
To check if the file exists and larger than 51200 bytes you could rewrite your if like this:
if find ehdbkp_$now -type f -size +51200c 2>/dev/null | grep -q .; then
echo "The backup has run successfully"
else
echo "The backup has was unsuccessful"
fi >> /var/log/backups
Other notes:
The find takes care two things at once: checks if file exists and size is greater than 51200.
We redirect stderr to /dev/null to hide the error message if the file doesn't exist.
If there was a file matching both conditions, then grep will match and exit with success, otherwise it will exit with failure
The final outcome of the grep is what decides the if condition
I moved the >> /var/log/backups after the closing fi, as it's equivalent this way and less duplication.
Btw if is NOT a loop, it's a conditional.
UPDATE
As #glennjackman pointed out, a better way to write the if, without grep:
if [[ $(find ehdbkp_$now -type f -size +51200c 2>/dev/null) ]]; then
...