Second subversion pre-commit hook not working - bash

I am trying to add a second pre-commit script and it seems not to be catching when I place it in the hook.
The first script basically locks a file from being editing. The second script look at a path and compares a string value to a file that is being committed and if it matches then it will error.
#!/bin/sh
REPOS="$1"
TXN="$2"
GREP=/bin/grep
SED=/bin/sed
AWK=/usr/bin/awk
SVNLOOK=/usr/bin/svnlook
AUTHOR=`$SVNLOOK author -t "$TXN" "$REPOS"`
if [ "$AUTHOR" == "testuser" ]; then
exit 0
fi
if [ "$AUTHOR" == "" ]; then
exit 0
fi
CHANGED=`$SVNLOOK changed -t "$TXN" "$REPOS" | $GREP "^[U|A]" | $AWK '{print $2}'`
COMPARE=`$SVNLOOK diff -t "$TXN" "$REPOS"`
#Operation 001 Beginning
#Restrict users from commiting against testfile
for PATH in $CHANGED
do
if [[ "$PATH" == *path/to/file/testfile.txt ]]; then
#allow testuser to have universal commit permissions in this path.
if [ "$AUTHOR" == "testuser" ]; then
exit 0
else
#User is trying to modify testfile.txt
echo "Only testuser can edit testfile.txt." 1>&2
exit 1
fi
fi
done
#Operation 001 Completed
#Operation 002 Beginning
#Restrict commits based on string found in file
for PATH in $COMPARE
do
if [[ "$PATH" == *path/to/look/at/only/* ]]; then
$SVNLOOK diff -t "$TXN" "$REPOS" | egrep 'string1|string2|string3' > /dev/null && { echo "Cannot commit using string1, string2 or string3 in files trying to commit" 1>&2; exit 1; }
else exit 0;
fi
done
#Operation 002 Completed
It keeps successfully committing the file even though the string is present. Any ideas why it wouldn't be catching it?

Your first test:
if [ "$AUTHOR" == "testuser" ]; then
exit 0
fi
It causes an abort (with zero exit value) if the AUTHOR is testuser!
So your second test:
if [ "$AUTHOR" == "testuser" ]; then
exit 0
else
#User is trying to modify testfile.txt
echo "Only testuser can edit testfile.txt." 1>&2
exit 1
fi
It's unnecessary because at this point the AUTHOR isn't testuser!
And maybe would better instead of your for-loop:
if $SVNLOOK changed -t "$TXN" "$REPOS" | $GREP "^[U|A]" | $AWK '{print $2}' | grep -q 'path/to/file/testfile.txt'; then
echo "Only testuser can edit testfile.txt." 1>&2
exit 1
fi
The if [[ "$PATH" == *path/to/file/testfile.txt ]]; then test doesn't work because this test doesn't understand shell variables (and would better enclose between quotation marks because of *).
And I would replace the
for PATH in $COMPARE
do
if [[ "$PATH" == *path/to/look/at/only/* ]]; then
part to
if echo ${COMPARE} | grep -q "path/to/look/at/only"; then

Related

How can I pipe output, from a command in an if statement, to a function?

I can't tell if something I'm trying here is simply impossible or if I'm really lacking knowledge in bash's syntax. This is the first script I've written.
I've got a Nextcloud instance that I am backing up daily using a script. I want to log the output of the script as it runs to a log file. This is working fine, but I wanted to see if I could also pipe the Nextcloud occ command's output to the log file too.
I've got an if statement here checking if the file scan fails:
if ! sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all; then
Print "Error: Failed to scan files. Are you in maintenance mode?"
fi
This works fine and I am able to handle the error if the system cannot execute the command. The error string above is sent to this function:
Print()
{
if [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "No" ]; then
echo "$1" | tee -a "$log_file"
elif [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "Yes" ]; then
echo "$1" >> "$log_file"
elif [[ "$logging" -eq 0 ]] && [ "$quiet_mode" = "No" ]; then
echo "$1"
fi
}
How can I make it so the output of the occ command is also piped to the Print() function so it can be logged to the console and log file?
I've tried piping the command after ! using | Print without success.
Any help would be appreciated, cheers!
The Print function doesn't read standard input so there's no point piping data to it. One possible way to do what you want with the current implementation of Print is:
if ! occ_output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1); then
Print "Error: Failed to scan files. Are you in maintenance mode?"
fi
Print "'occ' output: $occ_output"
Since there is only one line in the body of the if statement you could use || instead:
occ_output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1) \
|| Print "Error: Failed to scan files. Are you in maintenance mode?"
Print "'occ' output: $occ_output"
The 2>&1 causes both standard output and error output of occ to be captured to occ_output.
Note that the body of the Print function could be simplified to:
[[ $quiet_mode == No ]] && printf '%s\n' "$1"
(( logging )) && printf '%s\n' "$1" >> "$log_file"
See the accepted, and excellent, answer to Why is printf better than echo? for an explanation of why I replaced echo "$1" with printf '%s\n' "$1".
How's this? A bit unorthodox perhaps.
Print()
{
case $# in
0) cat;;
*) echo "$#";;
esac |
if [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "No" ]; then
tee -a "$log_file"
elif [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "Yes" ]; then
cat >> "$log_file"
elif [[ "$logging" -eq 0 ]] && [ "$quiet_mode" = "No" ]; then
cat
fi
}
With this, you can either
echo "hello mom" | Print
or
Print "hello mom"
and so your invocation could be refactored to
if ! sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all; then
echo "Error: Failed to scan files. Are you in maintenance mode?"
fi |
Print
The obvious drawback is that piping into a function loses the exit code of any failure earlier in the pipeline.
For a more traditional approach, keep your original Print definition and refactor the calling code to
if output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1); then
: nothing
else
Print "error $?: $output"
Print "Error: Failed to scan files. Are you in maintenance mode?"
fi
I would imagine that the error message will be printed to standard error, not standard output; hence the addition of 2>&1
I included the error code $? in the error message in case that would be useful.
Sending and receiving end of a pipe must be a process, typically represented by an executable command. An if statement is not a process. You can of course put such a statement into a process. For example,
echo a | (
if true
then
cat
fi )
causes cat to write a to stdout, because the parenthesis put it into a child process.
UPDATE: As was pointed out in a comment, the explicit subprocess is not needed. One can also do a
echo a | if true
then
cat
fi

Else if loop command depending on result string

I am trying to have a script take an argument and run a command against it and retry the command if a result matches a certain string. Otherwise other results will echo the result.
We have a script at work where we run "rm <filename>" but it takes 5 seconds to return whether successful or not and I want to make a script to keep running it until it sees a "destroyed file" string in the result.
This is what I have so far..
#!/bin/bash
function rm () {
if [[ $(rm $1 | grep -m 1 'destroyed file') ]] ; then
echo "Destroyed File"
elif [[ $(rm $1 | grep -m 1 'remove failed') ]] ; then
sleep 5;
rm $1
elif [[ $(rm $1 | grep -m 1 'not found') ]] ; then
echo "file not found"
elif [[ $(rm $1 | grep -m 1 'You must have root access') ]] ; then
echo "You do not have root access"
else
echo "Incorrect parameter or command"
fi
}

Getting command line argument that stores in a variable

I am writing a bash script to finger the first three line of user's info.
ex:
$ ./c.sh bob unknown
Login: bob Name: Bob
Directory: /u1/h7/bob Shell: /bin/tcsh
Office: AA 044, x8361 Home Phone: 000-000-0000
unknown: no such user.
Here is my code so far
#!/bin/bash
if [ $# == 0 ]; then
echo "Usage: ./c.sh Login/Username"
exit
else
i=$#
j=1
while [ "$j" -le "$i" ]; do
finger ${$j} | head -n+3
echo
j=$(($j+1))
done
fi
instead of giving what user types for the command line arguments, ${$j} is giving me the the value of $j, any suggestion and help for how to get the login/username? I've tried $($j), $((j)), ${$j}....
The easy answer: stop using unnecessary indirection:
#!/bin/bash
if (( $# == 0 )); then
echo "Usage: ./c.sh Login/Username"
exit
else
while [[ $1 ]]; do
finger "$1" | head -n+3
echo
shift
done
fi
or…
…
for user; do # equivalent to `for user in "$#"; do`
finger "$user" | head -n+3
…
done
You could write it this way:
i=$#
j=1
while [ $j -le $i ]; do
finger "${#:j++:1}" | head -n+3
echo
done
…but you don't need to work that hard.
#!/bin/bash
if [[ $# -eq 0 ]]; then
echo "Usage: $0 Login/Username"
exit
else
for ARG in "$#"; do
finger "$ARG" | head -n 3
echo # If you want a newline
done
fi
As simple as it can be.

Conditional statement bash script

I need help with replacing the following script with a different format where a configuration file, and a loop is used.
[FedoraC]$ cat script.sh
#!/bin/bash
grep -q /tmp /etc/fstab
if [ $? -eq 0 ]; then
echo "True"
else
echo "False"
fi
mount | grep ' /tmp' | grep nodev
if [ $? -eq 0 ]; then
echo "True"
else
echo "False"
fi
mount | grep /tmp | grep nosuid
if [ $? -eq 0 ]; then
echo "True"
else
echo "False"
fi
So far I have the following script which should take the values from a source/conf file and run each command found in the conf file one by one. After the command is executed the output would be "True" or "False"
conf file is formed by Unix commands: /opt/conf1
[FedoraC]$ cat conf1
grep -q /tmp /etc/fstab
mount | grep /tmp | grep nodev
mount | grep /tmp | grep nosuid
mount | grep /tmp | grep noexec
[FedoraC]$ cat new_script.sh
#!/bin/bash
. conf1
for i in $#;
do $i
if [ $i -eq 0 ]; then
echo "Passed"
else
echo "Failed"
fi
done
Instead of displaying the output based on the conditional statement, the script runs each line one by one from conf1, and not echo messages are seen.
Can I get some help please.
try this:
#! bin/bash
while read L; do
echo $L'; exit $?'|sh
if [ $? -eq 0 ]; then
echo Pass
else
echo Failed
fi
done < conf1
The more robust and canonical way to do this would be to have a directory /opt/conf1.d/, and put each of your lines as an executable script in this directory. You can then do
for file in /opt/conf1.d/*
do
[[ -x $file ]] || continue
if "$file"
then
echo "Passed"
else
echo "Failed"
fi
done
This has the advantages of supporting multi-line scripts, or scripts with more complex logic. It also lets you write the check script in any language, and lets scripts and packages add and remove contents easily and non-interactively.
If you really want to stick with your design, you can do it with:
while IFS= read -r line
do
if ( eval "$line" )
then
echo "Passed"
else
echo "Failed"
fi
done < /opt/conf1
The parentheses in the if statement runs eval in a subshell, so that lines can't interfere with each other by setting variables or exiting your entire loop.

Bash Loop and exit status check

An array holds the files accessed, and the archive files are split into smaller sizes in preparation for online backup. I am attempting to retrieve the exit code for each iteration through the loop of the split command. However, it is returning Exit Code 1, yet it says that the operation was successful. Why?
#!/bin/bash
declare -a SplitDirs
declare -a CFiles
CDIR=/mnt/Net_Pics/Working/Compressed/
SDIR=/mnt/Net_Pics/Working/Split/
Err=/mnt/Net_Pics/Working
SplitDirs=(`ls -l "$CDIR" --time-style="long-iso" | egrep '^d' | awk '{print $8}'`)
for dir in "${SplitDirs[#]}"
do
if [ ! -d "$SDIR""$dir" ]; then
mkdir "$SDIR""$dir"
else continue
fi
CFiles=(`ls -l "$CDIR$dir" --time-style="long-iso" | awk '{print $8}'`)
for f in "${CFiles[#]}"
do
if [ ! -e "$SDIR""$dir"/"$f" ]; then
split -d -a 4 -b 1992295 "$CDIR""$dir"/"$f" "$SDIR""$dir"/"$f" --verbose
if [[ "$?" == 1 ]]
then
rm -rf "$SDIR""$dir" && echo "$SDIR""$dir" "Removed due to Error code" "$?""." "Testing Archives and Retrying..." 2>&1 | tee "$Err"/Split_Err.log
7z t "$CDIR""$dir"/"$f" >> tee stdout.log 2>> "$Err"/"$dir"/7z_Err.log >&2
mkdir "$SDIR""$dir" && split -d -a 4 -b 1992295 "$CDIR""$dir"/"$f" "$SDIR""$dir"/"$f" --verbose
if [[ "$?" == 1 ]]
then
rm -rf "$SDIR""$dir" && echo "$SDIR""$dir" "Removed a second time due to Error code "$?". Skipping..." 2>&1 | tee "$Err"/Split_Err.log
continue
else
echo "Split Success:" "$SDIR""$dir"/"$f" "ended with Exit status" "$?" && continue
fi
else
echo "Split Success:" "$SDIR""$dir" "ended with Exit status" "$?" && continue
fi
else
echo "$SDIR""$dir"/"$f" "Exists... Skipping Operation" 2>&1 | tee "$Err"/"$dir"/Split_Err.log
continue
fi
done
(The echo piping in a previous revision of the question was misplaced code, and thank you for pointing that out. The exit code remains the same, though. Overall,the script does what I want it to except for the exit code portion.)
Remove | echo $?. you are processing the return code of echo command(last command).

Resources