awk variables print as options in bash - bash

I'm trying to make a script which reads your firefox profile.ini file, gives you options for which profile you want to unlock and execute a simple rm on the .lock file of the given profile (useful when you run multiple firefox sessions between workstations in different buildings and you did not logout correctly
I have the following file profile.ini for example
[General]
StartWithLastProfile=0
[Profile0]
Name=default
IsRelative=1
Path=rkfddmkn.default
Default=1
[Profile1]
Name=NX
IsRelative=1
Path=sf18055j.NX
[Profile14]
Name=gutter
IsRelative=1
Path=sf18055judsfsdfdfds.gutter
[Profile556]
Name=Jerusalem
IsRelative=1
Path=234920fgffdg38.Jerusalem
And this is the first part
ini=$HOME/.mozilla/firefox/profiles.ini
The script checks if profiles.ini exists otherwise exit
if [ -f $ini ]
then
The script reads the profiles.ini file and return multiple profiles as options followed by the name of the profiles
profiles=`cat $ini | grep "Profile[0-9*]"`
echo $profiles | awk -v RS=" " '{print}'
names=`cat $ini | grep Name | sed s/Name=/\/`
echo $names | awk -v RS=" " '{print}'
echo $options = $profiles . ' ' . $names; | awk -v RS=" " '{print}'
I'm not sure if I'm going down the right path.
How can I prompt the user to select an option by pairing the awk strings ?

A native-bash parser that breaks the relevant part of your input file into three associative arrays might look something like the following:
#!/usr/bin/env bash
set -x; PS4=':$LINENO+'
section_re='^\[Profile(.*)\][[:space:]]*$'
kv_re='^([[:alnum:]]+)=(.*)'
declare -A profilesByName=( )
declare -A profileNames=( )
declare -A profilePaths=( )
current_section=
while IFS= read -r line; do : line="$line"
[[ $line =~ $section_re ]] && { current_section=${BASH_REMATCH[1]}; continue; }
[[ $line =~ $kv_re ]] || continue
[[ $current_section ]] || continue ## ignore k/v pairs if not in a section
key=${BASH_REMATCH[1]}; value=${BASH_REMATCH[2]}
case $key in
Name) profileNames[$current_section]=$value
profilesByName[$value]=$current_section ;;
Path) profilePaths[$current_section]=$value ;;
esac
done
Then, if you want to delete the lockfile associated with a given profile name, it becomes as simple as:
deleteLockForName() {
local desiredName=$1
local selectedProfile selectedPath
selectedProfile=${profilesByName[$desiredName]}
[[ $selectedProfile ]] || { echo "ERROR: No profile with name $desiredName found" >&2; return 1; }
selectedPath=${profilePaths[$selectedProfile]}
echo "Deleting $selectedPath.lck" >&2
rm -f -- "$selectedPath.lck"
}
...as used in:
deleteLockForName Jerusalem
You can see it running at https://ideone.com/d0QFYa -- in the above example invocation, emitting Deleting 234920fgffdg38.Jerusalem.lck.

#CharlesDuffy is always a tough act to follow (try it...), but I took a somewhat different approach to solving your problem.
First, you don't need to deal with $HOME/.mozilla/firefox/profiles.ini at all. All you need to deal with is those profiles that have an existing lock symlink within their profile directory. So create an array just holding the names of the directories with with a lock symlink present to display to the user to remove. You can do that with find and sed in a simple command-substitution, e.g.
#!/bin/bash
moz="$HOME/.mozilla/firefox"
## locate all lockfile links that exist below "$moz"
readarray -t lockfiles < <(find "$moz" -type l -name lock -printf '%P\n')
Next, the select loop in bash will create a numbered menu with entries being the profile directories in your lockfiles array. The user can select the number corresponding to the profile directory name to remove (unlink) the lock symlink in that directory. For example:
## set prompt for select menu
PS3="Selection: "
## prompt
printf "\nThe following lockfiles found, select to remove, Quit to end:\n\n"
## create menu listing existing lockfile links
select lockname in "${lockfiles[#]%/*}" "Quit"; do
[ "$lockname" = "Quit" ] && break
if [ -h "$moz/$lockname/lock" ]; then
printf "\ndeleting lock %s\n" "$moz/$lockname/lock"
## uncomment to actually remove link
# unlink "$moz/$lockname/lock"
break
else
printf "\nerror: invalid selection.\n" >&2
fi
done
(note: setting PS3 controls the prompt displayed by the select loop, instead of the generic #?. Also note the /lock was trimmed from the contents of the array to display only the profile directory name in the select loop declaration with "${lockfiles[#]%/*}")
Putting it altogether, you would have:
#!/bin/bash
moz="$HOME/.mozilla/firefox"
## locate all lockfile links that exist below "$moz"
readarray -t lockfiles < <(find "$moz" -type l -name lock -printf '%P\n')
## set prompt for select menu
PS3="Selection: "
## prompt
printf "\nThe following lockfiles found, select to remove, Quit to end:\n\n"
## create menu listing existing lockfile links
select lockname in "${lockfiles[#]%/*}" "Quit"; do
[ "$lockname" = "Quit" ] && break
if [ -h "$moz/$lockname/lock" ]; then
printf "\ndeleting lock %s\n" "$moz/$lockname/lock"
## uncomment to actually remove link
# unlink "$moz/$lockname/lock"
break
else
printf "\nerror: invalid selection.\n" >&2
fi
done
Example Use/Output
$ bash ff_rm_lock.sh
The following lockfiles found, select to remove, Quit to end:
1) 3cblo6ag.dcr_new
2) Quit
Selection: 1
deleting lock /home/david/.mozilla/firefox/3cblo6ag.dcr_new/lock
or using "Quit" leaving all lock symlinks in place:
$ bash ff_rm_lock.sh
The following lockfiles found, select to remove, Quit to end:
1) 3cblo6ag.dcr_new
2) Quit
Selection: 2
(note: you must uncomment the line unlink "$moz/$lockname/lock" to actually remove the link -- I commented it to allow testing without removing my Firefox lockfile)
A different approach, but given your problem description, this should eliminate listing profiles with not associate lock symlink present. Let me know if you have questions.

Related

Bash simple script copying files to specific folder + renaming to todays effective date

Good day,
I need your help in creating next script
Every day teacher uploading files in next format:
STUDENT_ACCOUNTS_20200217074343-20200217.xlsx
STUDENT_MARKS_20200217074343-20200217.xlsx
STUNDENT_HOMEWORKS_20200217074343-20200217.xlsx
STUDENT_PHYSICAL_20200217074343-20200217.xlsx
SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx
[file_name+todaydatetime-todaydate.xlsx]
But sometimes a teacher is not uploading these files and we need to do manual renaming the files received for the previous date and then copying every separate file to separate folder like:
cp STUDENT_ACCOUNTS_20200217074343-20200217.xlsx /incoming/A1/STUDENT_ACCOUNTS_20200318074343-20200318.xlsx
cp STUDENT_MARKS_20200217074343-20200217.xlsx /incoming/B1/STUDENT_ACCOUNTS_20200318074343-20200318.xlsx
.............
cp SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx /incoming/F1/SUBSCRIBED_STUDENTS_20200318074343-20200318.xlsx.
In two words - taking the files from previous date copying them to specific folder with a new timestamp.
#!/bin/bash
cd /home/incoming/
date=$(date '+%Y%m%d')
previousdate="$( date --date=yesterday '+%Y%m%d' )"
cp /home/incoming/SUBSCRIBED_STUDENTS_'$previousdate'.xlsx /incoming/F1/SUBSCRIBED_STUDENTS_'$date'.xlsx
and there could be case when teacher can upload one file and others not, how to do check for existing files?
Thanks for reading that, if you can help me i will ne really thankful - you will save plenty of manual work for me.
The process can be automated completely if your directory structure is known. If it follows some kind of pattern, do mention it here.
For the timing, this maybe helpful:
Filename "tscp"
#
# Stands for timestamped cp
#
tscp() {
local file1=$1 ; shift
local to_dir=$1 ; shift
local force_copy=$1 ; shift
local current_date="$(date '+%Y%m%d')"
if [ "${force_copy}" == "--force" ] ; then
cp "${file1}" "${to_dir}/$(basename ${file1%-*})-${current_date}.xlsx"
else
cp -n "${file1}" "${to_dir}/$( basename ${file1%-*})-${current_date}.xlsx"
fi
}
tscp "$#"
It's usage is as follows:
tscp source to_directory [-—force]
Basically the script takes 2 arguments and the 3rd one is optional.
First arg is source file path and second are is the directory path to where you want to copy (. if same directory).
By default this copy would be made if and only if destination file doesn't exist.
If you want to overwrite the destination file then pass a third arg —force.
Again, this can be refined much much more based on details provided.
Sample usage for now:
bash tscp SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx /incoming/F1/
will copy SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx to directory /incoming/F1/ with updated date if it doesn't exist yet.
UPDATE:
Give this a go:
#! /usr/bin/env bash
printf_err() {
ERR_COLOR='\033[0;31m'
NORMAL_COLOR='\033[0m'
printf "${ERR_COLOR}$1${NORMAL_COLOR}" ; shift
printf "${ERR_COLOR}%s${NORMAL_COLOR}\n" "$#" >&2
}
alias printf_err='printf_err "Line ${LINENO}: " '
shopt -s expand_aliases
usage() {
printf_err \
"" \
"usage: ${BASH_SOURCE##*/} " \
" -f copy_data_file" \
" -d days_before" \
" -m months_before" \
" -o" \
" -y years_before" \
" -r " \
" -t to_dir" \
>&2
exit 1
}
fullpath() {
local path="$1" ; shift
local abs_path
if [ -z "${path}" ] ; then
printf_err "${BASH_SOURCE}: Line ${LINENO}: param1(path) is empty"
return 1
fi
abs_path="$( cd "$( dirname "${path}" )" ; pwd )/$( basename ${path} )"
printf "${abs_path}"
}
OVERWRITE=0
REVIEW=0
COPYSCRIPT="$( mktemp "/tmp/copyscriptXXXXX" )"
while getopts 'f:d:m:y:t:or' option
do
case "${option}" in
d)
DAYS="${OPTARG}"
;;
f)
INPUT_FILE="${OPTARG}"
;;
m)
MONTHS="${OPTARG}"
;;
t)
TO_DIR="${OPTARG}"
;;
y)
YEARS="${OPTARG}"
;;
o)
OVERWRITE=1
;;
r)
REVIEW=1
COPYSCRIPT="copyscript"
;;
*)
usage
;;
esac
done
INPUT_FILE=${INPUT_FILE:-$1}
TO_DIR=${TO_DIR:-$2}
if [ ! -f "${INPUT_FILE}" ] ; then
printf_err "No such file ${INPUT_FILE}"
usage
fi
DAYS="${DAYS:-1}"
MONTHS="${MONTHS:-0}"
YEARS="${YEARS:-0}"
if date -v -1d > /dev/null 2>&1; then
# BSD date
previous_date="$( date -v -${DAYS}d -v -${MONTHS}m -v -${YEARS}y '+%Y%m%d' )"
else
# GNU date
previous_date="$( date --date="-${DAYS} days -${MONTHS} months -${YEARS} years" '+%Y%m%d' )"
fi
current_date="$( date '+%Y%m%d' )"
tmpfile="$( mktemp "/tmp/dstnamesXXXXX" )"
awk -v to_replace="${previous_date}" -v replaced="${current_date}" '{
gsub(to_replace, replaced, $0)
print
}' ${INPUT_FILE} > "${tmpfile}"
paste ${INPUT_FILE} "${tmpfile}" |
while IFS=$'\t' read -r -a arr
do
src=${arr[0]}
dst=${arr[1]}
opt=${arr[2]}
if [ -n "${opt}" ] ; then
if [ ! -d "${dst}" ] ;
then
printf_err "No such directory ${dst}"
usage
fi
dst="${dst}/$( basename "${opt}" )"
else
if [ ! -d "${TO_DIR}" ] ;
then
printf_err "No such directory ${TO_DIR}"
usage
fi
dst="${TO_DIR}/$( basename "${dst}" )"
fi
src=$( fullpath "${src}" )
dst=$( fullpath "${dst}" )
if [ -n "${OVERWRITE}" ] ; then
echo "cp ${src} ${dst}"
else
echo "cp -n ${src} ${dst}"
fi
done > "${COPYSCRIPT}"
if [ "${REVIEW}" -eq 0 ] ; then
${BASH} "${COPYSCRIPT}"
rm "${COPYSCRIPT}"
fi
rm "${tmpfile}"
Steps:
Store the above script in a file, say `tscp`.
Now you need to create the input file for it.
From you example, a sample input file can be like:
STUDENT_ACCOUNTS_20200217074343-20200217.xlsx /incoming/A1/
STUDENT_MARKS_20200217074343-20200217.xlsx /incoming/B1/
STUNDENT_HOMEWORKS_20200217074343-20200217.xlsx
STUDENT_PHYSICAL_20200217074343-20200217.xlsx
SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx /incoming/FI/
Where first part is the source file name and after a "tab" (it should be a tab for sure), you mention the destination directory. These paths should be either absolute or relative the the directory where you are executing the script. You may not mention destination directory if all are to be sent to same directory (discussed later).
Let's say you named this file `file`.
Also, you don't really have to type all that. If you have these files in the current directory, just do this:
ls -1 > file
(the above is ls "one", not "l".)
Now we have the `file` from above in which we didn't mention destination directory for all but only for some.
Let's say we want to move all other directories to `/incoming/x` and it exists.
Now script is to be executed like:
bash tscp -f file -t /incoming/x -r
Where `/incoming/x` is the default directory i.e. when none other directory is mentioned in `file`, your files are moved to this directory.
Now in the current directory a script named `copyscript` will be generated which will contain `cp` commands to copy all files. You can open a review `copyscript` and if the copying seems right, go ahead and:
bash copyscript
which will copy all the files and then you can:
rm copyscript
You need not generate to `copyscript` and can straight away go for a copy like:
bash tscp -f file -t /incoming/x
which won't generate any copyscript and copy straight away.
Previously `-r` caused the generation of `copyscript`.
I would recomment to use version with `-r` because that is a little safer and you will be sure that right copies are being made.
By default it would check for the previous day and rename to current date, but you can override that behaviour as:
bash tscp -f file -t /incoming/x -d 3
`-d 3` would look for 3 days back files in `file`.
By default copies won't overwrite i.e. if file at the destination already exists, copies won't be made.
If you want to overwrite, add flag `-o`.
As a conclusion I would advice to use:
bash tscp -f file -r
where file contains tab separated values like above for all.
Also, adding tscp to path would be a good idea after you are sure it works ok.
Also the scipt is made on mac and there is always a change of version clash of tools used. I would suggest to try the script on some sample data first to make sure script works right on your machine.

(Ubuntu bash script) Setting rights from a config txt

I am a beginner and trying to write a script that takes a config file (example below) and sets the rights for the users, if that user or group doesn´t exist, they get added.
For every line in the file, I am cutting out the user or the group and check if they exist.
Right now I only check for users.
#!/bin/bash
function SetRights()
{
if [[ $# -eq 1 && -f $1 ]]
then
for line in $1
do
var1=$(cut -d: -f2 $line)
var2=$(cat /etc/passwd | grep $var1 | wc -l)
if [[ $var2 -eq 0 ]]
then
sudo useradd $var1
else
setfacl -m $line
fi
done
else
echo Enter the correct path of the configuration file.
fi
}
SetRights $1
The config file looks like this:
u:TestUser:- /home/temp
g:TestGroup:rw /home/temp/testFolder
u:TestUser2:r /home/temp/1234.txt
The output:
grep: TestGroup: No such file or directory
grep: TestUser: No such file or directory
"The useradd help menu"
If you could give me a hint what I should look for in my research, I would be very grateful.
Is it possible to reset var1 and var2? Using unset didn´t work for me and I couldn´t find variables could only be set once.
It's not clear how you are looping over the contents of the file -- if $1 contains the file name, you should not be seeing the errors you report.
But anyway, here is a refactored version which hopefully avoids your problems.
# Avoid Bash-only syntax for function definition
SetRights() {
# Indent function body
# Properly quote "$1"
if [[ $# -eq 1 && -f "$1" ]]
then
# Read lines in file
while read -r acl file
do
# Parse out user
user=${acl#*:}
user=${user%:*}
# Avoid useless use of cat
# Anchor regex correctly
if ! grep -q "^$user:" /etc/passwd
then
# Quote user
sudo useradd "$user"
else
setfacl -m "$acl" "$file"
fi
done <"$1"
else
# Error message to stderr
echo Enter the correct path of the configuration file. >&2
# Signal failure to the caller
return 1
fi
}
# Properly quote argument
SetRights "$1"

Nested for loop to enter and exit multiple directories Bash script

As an example, I have 7 directories each containing 4 files. The 4 files follow the following naming convention name_S#_L001_R1_001.fastq.gz. The sed command is to partially keep the unique file name.
I have a nested for loop in order to enter a directory and perform a command, exit the directory and proceed to the next directory. Everything seems to be working beautifully, however the code gets stuck on the last directory looping 4 times.
for f in /completepath/*
do
[ -d $f ] && cd "$f" && echo Entering into $f
for y in `ls *.fastq.gz | sed 's/_L00[1234]_R1_001.fastq.gz//g' | sort -u`
do
echo ${y}
done
done
Example output-
Entering into /completepath/m_i_cast_avpv_1
iavpvcast1_S6
Entering into /completepath/m_i_cast_avpv_2
iavpvcast2_S6
Entering into /completepath/m_i_int_avpv_1
iavpvint1_S5
Entering into /completepath/m_i_int_avpv_2
iavpvint2_S5
Entering into /completepath/m_p_cast_avpv_1
pavpvcast1_S8
Entering into /completepathd/m_p_int_avpv_1
pavpvint1_S7
Entering into /completepath/m_p_int_avpv_2
pavpvint2_S7
pavpvint2_S7
pavpvint2_S7
pavpvint2_S7
Any recommendations of how to correctly exit the inner loop?
It looks like /completepath/ contains some entries that are not directories. When the loop over /completepath/* sees something that's not a directory, it doesn't enter it, thanks to the [ -d $f ] check.
But it still continues to run the next for y in ... loop.
At that point the script is still in the previous directory it has seen.
One way to solve that is to skip the rest of the loop when $f is not a directory:
if [ -d $f ]; then
cd "$f" && echo Entering into $f
else
continue
fi
There's an even better way. By writing /completepath/*/ only directory entries will be matched, so you can simplify your loop to this:
for f in /completepath/*/
do
cd "$f" && echo "Entering into $f" || { echo "Error: could not enter into $f"; continue; }
for y in $(ls *.fastq.gz | sed 's/_L00[1234]_R1_001.fastq.gz//g' | sort -u)
do
echo ${y}
done
done

Awk skip file if it doesn't exist

I've spent some time trying to figure this out with various internet searches and digging through stackoverflow. I'll try to explain this as best as a noob can.
I have a script that searches a config repository directory that is populated with directories for every Juniper and Cisco router and switch we have deployed. Each device directory has a file or two that I'm interested in, "show.version" and "show.chassis.hardware", except when they don't. The second file, "show.chassis.hardware" is not a command that Cisco boxes has, so that file does not exist in Cisco device directories. There also isn't a naming scheme that can easily tell me if the device is Juniper or Cisco, which is also part of the reason why my script exists.
To make things more fun, different models and even software versions output the show.version file in different formats (for both Cisco and Juniper), so my awk is full of all the different fields we will encounter.
Script:
#!/usr/local/bin/zsh
svn="$HOME/svn/nw_config_data/"
case "$1" in
("-a")
hosts=""
;;
("-b")
hosts=".bb.domain.net"
;;
("-c")
hosts=".cpe.domain.net"
;;
("-e")
hosts=".etech.domain.net"
;;
("-k")
hosts=".core.domain.net"
;;
("-m")
hosts=".maint.domain.net"
;;
("-o")
hosts=".ohgov.domain.net"
;;
esac
dirs=($(ls -d $svn*$hosts*))
for hostdir in $dirs
do host=$(echo $hostdir | grep -Eo "(\w|\.|-)*$")
awk -v h=$host '/^Model/{m=$2} /^Model number/{m=$4} /^\*0/{m=$2}
/^JUNOS Base OS boot/{v=$5} /^Junos:/{v="["$2"]"} /^BOOTLDR:/{v=$7}
/^JUNOS EX Software Suite/{v=$5} /^ROM:/{v=$5} /^JUNOS Software
Release/{v=$4} /^Chassis/{s=$2} /^Motherboard serial number/{s=$3}
END {if(m!="number") {printf("%s %s %s %s\n",h,m,v,s)}}'
"$hostdir/show.version" "$hostdir/show.chassis.hardware"
done
What it looks like when I run the script:
% cver -b
device1-e0.bb.domain.net ex4300-24t [14.1X53-D25.2] Serial#
device2-e0.bb.domain.net ex4300-24t [14.1X53-D25.2] Serial#
awk: can't open file /home/clmbn eng2/a/rwalker/svn/nw_config_data/device3-e1.bb.domain.net/show.chassis.hardware
input record number 55, file /home/clmbn-eng2/a/rwalker/svn/nw_config_data/device3-e1.bb.domain.net/show.chassis.hardware
source line number 1
device4-r0.bb.domain.net m7i [13.3R6.5] Serial#
...
What I want it to look like
% cver -b
device1-e0.bb.domain.net ex4300-24t [14.1X53-D25.2] Serial#
device2-e0.bb.domain.net ex4300-24t [14.1X53-D25.2] Serial#
device3-e1.bb.domain.net C3750 12.1(14r)EA1a, Serial#
device4-r0.bb.domain.net m7i [13.3R6.5] Serial#
...
I have 13 directories that do not have the "show.chassis.hardware" file but do have the "show.version" file which does have all the information I need from it. I have one directory that has no files, but it doesn't matter because that device is going to be replaced.
From what I've been reading, awk might not be able to do this, but I have faith that someone out there knows a way to make it work. If my approach (shell & awk scripting) just isn't going to work and I need to do it in something else (Perl or Python for example) I'll be completely stuck until I can learn those enough to convert my script to one of those languages.
Also, we don't have bash installed on this server and I don't know when we will since I'm not the admin.
You need
-f file
true if file exists and is a regular file.
if [[ -f "$hostdir/show.version" && -f "$hostdir/show.chassis.hardware" ]]; then
# your awk command goes here...
awk '{ }' "$hostdir/show.version" "$hostdir/show.chassis.hardware"
else
echo "not enough files found"
fi
You may refer : http://zsh.sourceforge.net/Doc/Release/Conditional-Expressions.html
--edit--
That's cool that this bit of script skips the directories that doesn't
have the file, but it doesn't pull the information from the
"show.version" file and print that information anyway. So the output
shows device1, device2, device 4...
Here is code snippet
function myfunc(){
# replace with your awk
awk '{ print }' "$#"
}
if [[ -f "$hostdir/show.version" && -f "$hostdir/show.chassis.hardware" ]]; then
# your awk command goes here...
myfunc "$hostdir/show.version" "$hostdir/show.chassis.hardware"
else
echo "not enough files found"
# pass only one file, version file
myfunc "$hostdir/show.version"
fi
You can use zsh's (...|...) globbing:
$ mkdir foo bar; touch {foo,bar}/show.version foo/show.chassis.hardware
$ echo foo/show.(version|chassis.hardware)
foo/show.chassis.hardware foo/show.version
$ echo bar/show.(version|chassis.hardware)
bar/show.version
Since this is globbing, it will only expand to existing files. So your awk command would look like:
awk -v h=$host '/^Model/{m=$2} ... END {...}' "$hostdir"/show.(version|chassis.hardware)
(omitting the awk script for readability)
I'd also simplify your script a bit using associative arrays instead of cases:
#!/usr/local/bin/zsh
usage () {
echo "Help!" # echo your help message here
exit $1
}
svn="$HOME/svn/nw_config_data/"
declare -A hosts # make associative array
hosts["-a"]=""
hosts["-b"]=".bb.domain.net"
hosts["-c"]=".cpe.domain.net"
hosts["-e"]=".etech.domain.net"
hosts["-k"]=".core.domain.net"
hosts["-m"]=".maint.domain.net"
hosts["-o"]=".ohgov.domain.net"
if [[ $1 == -h ]]
then
usage
elif (( ${+hosts["$1"]} )) # check if $1 is a key in hosts
then
echo "Invalid option: $1"
usage 1 # exit with status 1 to indicate error
fi
dirs=( $svn*$hosts["$1"]* ) # no need for ls here
for hostdir in $dirs
do
host=$(echo $hostdir | grep -Eo "(\w|\.|-)*$")
awk -v h=$host '
/^Model/{m=$2}
/^Model number/{m=$4}
/^\*0/{m=$2}
/^JUNOS Base OS boot/{v=$5}
/^Junos:/{v="["$2"]"}
/^BOOTLDR:/{v=$7}
/^JUNOS EX Software Suite/{v=$5}
/^ROM:/{v=$5}
/^JUNOS Software Release/{v=$4}
/^Chassis/{s=$2}
/^Motherboard serial number/{s=$3}
END {if(m!="number") {printf("%s %s %s %s\n",h,m,v,s)}}' \
"$hostdir"/show.(version|chassis.hardware)
done
Alternately, you can use a concise case with the array:
declare -A hosts # make associative array
hosts["-a"]=""
hosts["-b"]=".bb.domain.net"
hosts["-c"]=".cpe.domain.net"
hosts["-e"]=".etech.domain.net"
hosts["-k"]=".core.domain.net"
hosts["-m"]=".maint.domain.net"
hosts["-o"]=".ohgov.domain.net"
case $1 in
-[abcekmno]) dirs=( $svn*${hosts["$1"]}* )
;;
-h|help) usage
;;
*) echo "Invalid option: $1"
usage 1 # exit with status 1 to indicate error
;;
esac

Copy contents of multiple directories inside current directory into new directory

Brand new to .sh and I'm trying to backup images from an old iPhone where images were placed into a new directory for date. There are hundreds of directories where I need to take the pictures from each and dump them all into one directory.
My best attempt:
#!/bin/bash
function process () {
a=1
for i in *
do
cp -r i ${dir}
let a=a+1
done
}
#Interview
echo "This script will take the contents of each directory in the current directory and move it's contents into a new directory that you will specify"
echo "Name your new directory"
read dir
#Confirm
read -p "Your new directory will be ${dir}. Continue?" -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]
then
process
fi
Errors recieved:
massmove.sh: line 1: $'\r': command not found
massmove.sh: line 2: $'\r': command not found
massmove.sh: line 3: $'\r': command not found
massmove.sh: line 4: syntax error near unexpected token `$'{\r''
'assmove.sh: line 4: `function process () {
Update: Improved via deefff's answer:
function process () {
for i in */*.*
do
cp -r $i ${dir}
done
}
echo "This script will take the contents of each directory in the current directory and mv it's contents into a new directory that you will specify"
echo "Name your new directory"
read dir
mkdir ${dir}
echo process
Still is throwing these errors:
massmove.sh: line 2: syntax error near unexpected token `$'{\r''
'assmove.sh: line 2: `function process () {
Could this be a WSL bug?
I understand that
cp */*.* ${dir}
is a quick and powerful way to accomplish my task but i'm also very interested in what is causing the errors.
* will match every entry, including directories, so in its current form, it will copy the whole directory structure, which I guess, is not what you want. Also, you should refer to the variable i as $i in the cp command. Also, the a variable seems pointless. Try it like this:
function process () {
for i in */*.*
do
cp $i ${dir}
done
}
In fact, I just realized that this should do the trick as well:
cp */*.* ${dir}
Please see my script below.
It basically has 3 functions: menu, prnt (for printing lines with date) and process (to process files). It has a menu, which works with options. Hope you like it.
Cheers !!
Gaurav
#!/bin/bash
curdir=`pwd` # Take the current directory
# To print out logs on the screen, with date and time !!!
prnt ()
{
d=`date +"%d-%m-%y "%T` # Take the date string using bash date function.
echo "$d|$1" # Whenever prnt is called, it will echo back, date plus the line passed to it, in nice date|line format.
}
# Menu for the whole operation. This is just the definition. Its being called from the bottom of the script.
menu()
{
# Specially made for you my friend !!
echo ; echo ; prnt " <<!!*** .. params_noob's file copier .. ***!!>>" ; echo ; echo
echo ; prnt "Currently we are in $curdir" ; echo
prnt "Enter the directory, where you would like to move files (Please enter full path and system will create it): " ; echo
read dir ; mkdir $dir 2>/dev/null # Read directory and make it. I am putting all errors to /dev/null. If directory is there, its there, don't want blabber. If not, then create it.
echo ;prnt "Entered directory is \"$dir\"" ; echo
prnt "Type y to go ahead OR n to start over again.." ; echo
read ans # I have read the answer here
# A simple menu using case function of bash.
case $ans in
y)
echo ; prnt "You have entered yes, to move ahead" ; echo
prnt "processing files now...."
process $dir # Here i am calling the process function.
;;
n)
echo ; prnt "Starting over.. " ; echo
menu # Here i am calling the menu itself, again.
;;
0)
echo ; prnt "Exiting now.. " ; echo
exit # Now exiting the script.
;;
*)
# This one is just to let you know, if you enter anything else than y,n or 0.
echo ; prnt "Invalid input. Please enter y or n or 0 !!" ; echo
;;
esac # Menu using case function ends with esac here.
}
# Function to process and move the files to the desired location.
process()
{
# Took the argument passed to this function into variable "td" [target dirctory].
td="$1"
# Using find command here to find all files. You an replace '-type f' with '-name "*.pic"' or '-name "*.jpg"' -- you get the picture - right?
find $curdir -type f | while read file # Feeding output of find to a while loop.
do
cp $file $td/
done
a=`ls -lrt $td/* |wc -l` # Now taking word count of all the files copied.
# Some more echo and priting.. just for aesthetics.. :)
echo ; echo ; prnt " **** COPIED $a FILES from \[$curdir\] to \[$td\] ****" ; echo ; echo
echo ; echo ; ls -lrtha $td|head -10 ; echo ; echo
exit # Script exits after processing the files. You can replace exit with menu, to go back to menu again.
}
#
##
###
####
#####
#################################################################
clear ; menu ### The main menu is being called from this line. I clear the screen first. Looks more professional :)
#################################################################
# FINISH # All done !!
enter code here
Here is the output in a picture format
Try something like this:
function process () {
for file in *; do
if [[ ${file} != ${dir} ]]; then
cp -r ${file} ${dir}
fi
done
}
Also, remember to create the directory before you call the 'process' function:
echo "Name your new directory: "
read dir
mkdir ${dir}

Resources