Smarter Vim recovery? - vim
When a previous Vim session crashed, you are greeted with the "Swap file ... already exists!" for each and every file that was open in the previous session.
Can you make this Vim recovery prompt smarter? (Without switching off recovery!) Specifically, I'm thinking of:
If the swapped version does not contain unsaved changes and the editing process is no longer running, can you make Vim automatically delete the swap file?
Can you automate the suggested process of saving the recovered file under a new name, merging it with file on disk and then deleting the old swap file, so that minimal interaction is required? Especially when the swap version and the disk version are the same, everything should be automatic.
I discovered the SwapExists autocommand but I don't know if it can help with these tasks.
I have vim store my swap files in a single local directory, by having this in my .vimrc:
set directory=~/.vim/swap,.
Among other benefits, this makes the swap files easy to find all at once.
Now when my laptop loses power or whatever and I start back up with a bunch of swap files laying around, I just run my cleanswap script:
TMPDIR=$(mktemp -d) || exit 1
RECTXT="$TMPDIR/vim.recovery.$USER.txt"
RECFN="$TMPDIR/vim.recovery.$USER.fn"
trap 'rm -f "$RECTXT" "$RECFN"; rmdir "$TMPDIR"' 0 1 2 3 15
for q in ~/.vim/swap/.*sw? ~/.vim/swap/*; do
[[ -f $q ]] || continue
rm -f "$RECTXT" "$RECFN"
vim -X -r "$q" \
-c "w! $RECTXT" \
-c "let fn=expand('%')" \
-c "new $RECFN" \
-c "exec setline( 1, fn )" \
-c w\! \
-c "qa"
if [[ ! -f $RECFN ]]; then
echo "nothing to recover from $q"
rm -f "$q"
continue
fi
CRNT="$(cat $RECFN)"
if diff --strip-trailing-cr --brief "$CRNT" "$RECTXT"; then
echo "removing redundant $q"
echo " for $CRNT"
rm -f "$q"
else
echo $q contains changes
vim -n -d "$CRNT" "$RECTXT"
rm -i "$q" || exit
fi
done
This will remove any swap files that are up-to-date with the real files. Any that don't match are brought up in a vimdiff window so I can merge in my unsaved changes.
--Chouser
I just discovered this:
http://vimdoc.sourceforge.net/htmldoc/diff.html#:DiffOrig
I copied and pasted the DiffOrig command into my .vimrc file and it works like a charm. This greatly eases the recovery of swap files. I have no idea why it isn't included by default in VIM.
Here's the command for those who are in a hurry:
command DiffOrig vert new | set bt=nofile | r # | 0d_ | diffthis
\ | wincmd p | diffthis
The accepted answer is busted for a very important use case. Let's say you create a new buffer and type for 2 hours without ever saving, then your laptop crashes. If you run the suggested script it will delete your one and only record, the .swp swap file. I'm not sure what the right fix is, but it looks like the diff command ends up comparing the same file to itself in this case. The edited version below checks for this case and gives the user a chance to save the file somewhere.
#!/bin/bash
SWAP_FILE_DIR=~/temp/vim_swp
IFS=$'\n'
TMPDIR=$(mktemp -d) || exit 1
RECTXT="$TMPDIR/vim.recovery.$USER.txt"
RECFN="$TMPDIR/vim.recovery.$USER.fn"
trap 'rm -f "$RECTXT" "$RECFN"; rmdir "$TMPDIR"' 0 1 2 3 15
for q in $SWAP_FILE_DIR/.*sw? $SWAP_FILE_DIR/*; do
echo $q
[[ -f $q ]] || continue
rm -f "$RECTXT" "$RECFN"
vim -X -r "$q" \
-c "w! $RECTXT" \
-c "let fn=expand('%')" \
-c "new $RECFN" \
-c "exec setline( 1, fn )" \
-c w\! \
-c "qa"
if [[ ! -f $RECFN ]]; then
echo "nothing to recover from $q"
rm -f "$q"
continue
fi
CRNT="$(cat $RECFN)"
if [ "$CRNT" = "$RECTXT" ]; then
echo "Can't find original file. Press enter to open vim so you can save the file. The swap file will be deleted afterward!"
read
vim "$CRNT"
rm -f "$q"
else if diff --strip-trailing-cr --brief "$CRNT" "$RECTXT"; then
echo "Removing redundant $q"
echo " for $CRNT"
rm -f "$q"
else
echo $q contains changes, or there may be no original saved file
vim -n -d "$CRNT" "$RECTXT"
rm -i "$q" || exit
fi
fi
done
Great tip DiffOrig is perfect. Here is a bash script I use to run it on each swap file under the current directory:
#!/bin/bash
swap_files=`find . -name "*.swp"`
for s in $swap_files ; do
orig_file=`echo $s | perl -pe 's!/\.([^/]*).swp$!/$1!' `
echo "Editing $orig_file"
sleep 1
vim -r $orig_file -c "DiffOrig"
echo -n " Ok to delete swap file? [y/n] "
read resp
if [ "$resp" == "y" ] ; then
echo " Deleting $s"
rm $s
fi
done
Probably could use some more error checking and quoting but has worked so far.
I prefer to not set my VIM working directory in the .vimrc. Here's a modification of chouser's script that copies the swap files to the swap path on demand checking for duplicates and then reconciles them. This was written rushed, make sure to evaluate it before putting it to practical use.
#!/bin/bash
if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
echo "Moves VIM swap files under <base-path> to ~/.vim/swap and reconciles differences"
echo "usage: $0 <base-path>"
exit 0
fi
if [ -z "$1" ] || [ ! -d "$1" ]; then
echo "directory path not provided or invalid, see $0 -h"
exit 1
fi
echo looking for duplicate file names in hierarchy
swaps="$(find $1 -name '.*.swp' | while read file; do echo $(basename $file); done | sort | uniq -c | egrep -v "^[[:space:]]*1")"
if [ -z "$swaps" ]; then
echo no duplicates found
files=$(find $1 -name '.*.swp')
if [ ! -d ~/.vim/swap ]; then mkdir ~/.vim/swap; fi
echo "moving files to swap space ~./vim/swap"
mv $files ~/.vim/swap
echo "executing reconciliation"
TMPDIR=$(mktemp -d) || exit 1
RECTXT="$TMPDIR/vim.recovery.$USER.txt"
RECFN="$TMPDIR/vim.recovery.$USER.fn"
trap 'rm -f "$RECTXT" "$RECFN"; rmdir "$TMPDIR"' 0 1 2 3 15
for q in ~/.vim/swap/.*sw? ~/.vim/swap/*; do
[[ -f $q ]] || continue
rm -f "$RECTXT" "$RECFN"
vim -X -r "$q" \
-c "w! $RECTXT" \
-c "let fn=expand('%')" \
-c "new $RECFN" \
-c "exec setline( 1, fn )" \
-c w\! \
-c "qa"
if [[ ! -f $RECFN ]]; then
echo "nothing to recover from $q"
rm -f "$q"
continue
fi
CRNT="$(cat $RECFN)"
if diff --strip-trailing-cr --brief "$CRNT" "$RECTXT"; then
echo "removing redundant $q"
echo " for $CRNT"
rm -f "$q"
else
echo $q contains changes
vim -n -d "$CRNT" "$RECTXT"
rm -i "$q" || exit
fi
done
else
echo duplicates found, please address their swap reconciliation manually:
find $1 -name '.*.swp' | while read file; do echo $(basename $file); done | sort | uniq -c | egrep '^[[:space:]]*[2-9][0-9]*.*'
fi
I have this on my .bashrc file. I would like to give appropriate credit to part of this code but I forgot where I got it from.
mswpclean(){
for i in `find -L -name '*swp'`
do
swpf=$i
aux=${swpf//"/."/"/"}
orif=${aux//.swp/}
bakf=${aux//.swp/.sbak}
vim -r $swpf -c ":wq! $bakf" && rm $swpf
if cmp "$bakf" "$orif" -s
then rm $bakf && echo "Swap file was not different: Deleted" $swpf
else vimdiff $bakf $orif
fi
done
for i in `find -L -name '*sbak'`
do
bakf=$i
orif=${bakf//.sbak/}
if test $orif -nt $bakf
then rm $bakf && echo "Backup file deleted:" $bakf
else echo "Backup file kept as:" $bakf
fi
done }
I just run this on the root of my project and, IF the file is different, it opens vim diff. Then, the last file to be saved will be kept. To make it perfect I would just need to replace the last else:
else echo "Backup file kept as:" $bakf
by something like
else vim $bakf -c ":wq! $orif" && echo "Backup file kept and saved as:" $orif
but I didn't get time to properly test it.
Hope it helps.
find ./ -type f -name ".*sw[klmnop]" -delete
Credit: #Shwaydogg
https://superuser.com/questions/480367/whats-the-easiest-way-to-delete-vim-swapfiles-ive-already-recovered-from
Navigate to directory first
Related
Recycle Bin in Bash Script
I am trying to create a basic recycle bin concept in a VM using bash scripting. It will need to delete files that have been entered and place them into a directory that is created and save the path(origin) to a log file to be later used in a restore function. I will start off with my delete/recycle code which I believe works just fine but seems kind of untidy/contains redundant code: #!/bin/sh if [ ! -d ~/recycle ] then mkdir ~/recycle fi if [ ! -d ~/recycle/recycle_log ] then mkdir ~/recycle/recycle_log fi if [ ! -d ~/recycle/recycle_bin ] then mkdir ~/recycle/recycle_bin fi if [ -d ~/recycle ] then echo "$(readlink -f "$1")" >> "$HOME/recycle/recycle_log/log_file" && mv "$1" "$HOME/recycle/recycle_bin" echo "$(readlink -f "$2")" >> "$HOME/recycle/recycle_log/log_file" && mv "$2" "$HOME/recycle/recycle_bin" echo "$(readlink -f "$3")" >> "$HOME/recycle/recycle_log/log_file" && mv "$3" "$HOME/recycle/recycle_bin" echo "$(readlink -f "$4")" >> "$HOME/recycle/recycle_log/log_file" && mv "$4" "$HOME/recycle/recycle_bin" fi #end Thereafter what I have for my restore script is as follows: #!/bin/sh cd "$HOME/recycle/recycle_bin" || exit 1 mv -i "$(grep "$1" "$HOME/recycle/recycle_log")" I imagine this is somewhat close to what I need to return any deleted file stored in the log/recycle bin to be restored to its origin but the error I am getting is: mv: missing destination file operand after `' Any thoughts on where I'm going wrong?
Try this: recycle.sh #!/bin/sh set -e check_dir() { [ ! -d $1 ] || return 0 mkdir --parents $1 } check_dir "${HOME}/recycle/recycle_bin" touch "${HOME}/recycle/recycle_log" for file in "$#"; do echo "$(readlink -f "$file")" >> "${HOME}/recycle/recycle_log" mv "$file" "${HOME}/recycle/recycle_bin" done #end restore.sh #!/bin/sh set -e cd "${HOME}/recycle/recycle_bin" || exit 1 for name in "$#"; do file=$(grep "\/${name}\$" "${HOME}/recycle/recycle_log") mv -i $name "$file" sed -i "/\/${name}\$/ d" "${HOME}/recycle/recycle_log" done Some insights: set -e: Abort on any error, to avoid some if's $#: The array of arguments ($1, $2...) [ ! -d $1 ] || return 0: Since we are using set -e, do not fail if the directory exists grep "\/${name}\$" ...: Only matches the name at the end of the path sed -i: sed in-place editing to remove the line
errors running sh script [closed]
Closed. This question needs debugging details. It is not currently accepting answers. Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question. Closed 7 years ago. Improve this question I keep getting these errors running my script and i just cannot work it out... the error that keeps coming up is; rm: cannot remove ~/my-documents/article:': Is a directory. The directory its referring to is $2...here is my script. #! /bin/sh SRC=$1 DES=$2 if [ $# -ne 2 ]; then echo "1. Please enter the source directory" echo "2. Please enter the destination directory" echo "thankyou" exit fi if [ ! -d $1 ]; then echo "$1 is not a directory please enter a valid directory" echo "thankyou" exit fi #gives the user a error warning the source directory is invalid if [ -d $2 ]; then echo "output directory exists" else echo "Output directory does not exist, creating directory" mkdir $2 fi #creates the destination directory if one doesn't exist IFILE=$GETFILES; FINDFILE=$FINDFILE; find $1 -name "*.doc" > FINDFILE find $1 -name "*.pdf" > FINDFILE find $1 -name "*.PDF" > FINDFILE #finds doc, pdf & PDF files and sends data to findfile. while read -r line; do cp $line $2 done < FINDFILE #files read and copied to destination directory IFILE=$2/$GETFILES; ls -R $1 | egrep -i ".doc | .pdf" > IFILE; LCOUNT=0 DIFFCOUNT=0 FOUND=0 ARCHIVE=1 BASE="${line%.*}" EXTENSION="${line##*.}" COUNT=$COUNT; ls $2 | grep ${line%%.*} \; | wc -l if [[ $COUNT -eq 0 ]]; then cp $1/$line $2; else echo "there is already a file in the output so need to compare" COMP=$2/$line fi while [[ $FOUND -eq 0 ]] && [[ $LCOUNT -lt $COUNT ]]; do echo "diffcount is $DIFFCOUNT" ###compares the file from the input directory to the file in ###the output directory if [ $DIFFCOUNT -eq 0 ]; then echo "file has already been archived no action required" FOUND=$FOUND [ $FOUND+1 ] else LCOUNT=$LCOUNT [ $LCOUNT+1 ] COMP="OUT"/"$BASE"_"$LCOUNT"."$EXTENSION" echo "line count for next compare is $LCOUNT" echo "get the next file to compare" echo "the comparison file is now $COMP" fi if [ $LCOUNT -ne $COUNT ]; then ARCHIVE=$ [ $ARCHIVE+1 ] else ARCHIVE=0 fi if [ $ARCHIVE -eq 0 ]; then NEWOUT="OUT"/"$BASE"_"$LCOUNT"."$EXTENSION"; echo "newfile name is $NEWOUT" cp $1/$LINE $NEWOUT fi done < $IFILE rm $IFILE OFILE=$2/DOCFILES; ls $2 | grep ".doc" > $OFILE; while read -r line; do BASE=${line%.*} EXTENSION=${line##*.} NEWEXTENSION=".pdf" SEARCHFILE=$BASE$NEWEXTENSION find $2 -name "$SEARCHFILE" -exec {} \; done < $OFILE rm $OFILE ### this will then remove any duplicate files so only ### individual .doc .pdf files will exist
a plain call to rm can only remove files, not directories. $ touch /tmp/myfile $ rm /tmp/myfile $ mkdir /tmp/mydir $ rm /tmp/mydir rm: cannot remove ‘/tmp/mydir/’: Is a directory You can remove directories by specifying the -d (to delete empty directories) or the -r (to delete directories and content recursively) flag: $ mkdir /tmp/mydir $ rm -r /tmp/mydir $ this is well described in man rm. apart from that, you seem to ignore quoting: $ rm $OFILE might break badly if the value of OFILE contains spaces, use quotes instead: $ rm "${OFILE}" and never parse the output of ls: ls $2 | grep ".doc" > $OFILE (e.g. if your "$2" is actually "/home/foo/my.doc.files/" it will put all files in this directory into $OFILE). and then you iterate over the contents of this file? instead, just use loop with file-globbing: for o in "${2}"/*.doc do ## loop code in here done or just do the filtering with find (and don't forget to call an executable with -exex): find "$2" -name "$SEARCHFILE" -mindepth 1 -maxdepth 1 -type f -exec convertfile \{\} \;
remove files and prompt directories only
As I was deleting many obsolete file trees on a Linux machine I was wondering if there is an easy way to remove files recursively while prompting only on directories. I could use rm -ri but there some much files that it would be really annoying to answer for every one of them. What really matter to me is being prompted on folders to have more control on what happens. I am not a bash expert so I am asking if there is a simple way to do this. Here is my attempt with a long bash script: #!/bin/bash promptRemoveDir() { fileCount=$(ls -1 $1 | wc -l) prompt=1 while [ $prompt == 1 ] do read -p "remove directory: $1($fileCount files) ? [yl]: " answer case $answer in [yY]) rm -r $1 prompt=0 ;; l) echo $(ls -A $1) ;; *) prompt=0 ;; esac done } removeDir() { if [ "$(ls -A $1)" ] then dirs=$(find $1/* -maxdepth 0 -type d) fi if [[ -z $dirs ]] then promptRemoveDir $1 else for dir in $dirs do removeDir $dir done promptRemoveDir $1 fi } for i in $* do if [ -d $i ] then removeDir $i else rm $i fi done
If i understand your question properly this should work Dirs=$(find . -type d) Removes just the files in the directories specified for i in "$Dirs"; do read -p "Delete files in "$i": ";if [[ $REPLY == [yY] ]]; then find $i -maxdepth 1 -type f | xargs -0 rm ; fi ;done If you want to delete the folders as well, this will read from lowest directory(none below it) upwards. for i in $(echo "$Dirs" | sed '1!G;h;$!d' ); do read -p "Delete files in $i: ";if [[ $REPLY == [yY] ]]; then rm -r "$i"; fi ;done
Here's a simplified version from me. There's no need to use ls and find. #!/bin/bash shopt -s nullglob shopt -s dotglob function remove_dir_i { local DIR=$1 ## Optional. We can just use $1. local SUBFILES=("$DIR"/*) FILE for (( ;; )); do read -p "Remove directory: $DIR (${#SUBFILES[#]} files)? [YNLQ]: " case "$REPLY" in [yY]) echo rm -fr "$DIR" return 0 ;; [nN]) for FILE in "${SUBFILES[#]}"; do if [[ -d $FILE ]]; then remove_dir_i "$FILE" || return 1 # else # ## Apparently we skip deleting a file. If we do this # ## we could actually simplify the function further # ## since we also delete the file at first loop. # # echo "Removing file \"$FILE.\"" # # rm -f "$FILE" fi done return 0 ;; [lL]) printf '%s\n' "${SUBFILES[#]}" ;; [qQ]) return 1 ;; # *) # echo "Please answer Y(es), N(o), L(ist) or Q(uit)." # ;; esac done } for FILE; do if [[ -d $FILE ]]; then remove_dir_i "$FILE" else # echo "Removing file \"$FILE.\"" echo rm -f "$FILE" fi done Remove echo from rm commands when you're sure it's working already. Test: rm -f /tmp/tar-1.27.1/ABOUT-NLS rm -f /tmp/tar-1.27.1/acinclude.m4 rm -f /tmp/tar-1.27.1/aclocal.m4 rm -f /tmp/tar-1.27.1/AUTHORS Remove directory: /tmp/tar-1.27.1/build-aux (12 files)? [YNLQ]: n Remove directory: /tmp/tar-1.27.1/build-aux/snippet (5 files)? [YNLQ]: n rm -f /tmp/tar-1.27.1/ChangeLog rm -f /tmp/tar-1.27.1/ChangeLog.1 rm -f /tmp/tar-1.27.1/config.h.in rm -f /tmp/tar-1.27.1/configure rm -f /tmp/tar-1.27.1/configure.ac rm -f /tmp/tar-1.27.1/COPYING Remove directory: /tmp/tar-1.27.1/doc (25 files)? [YNLQ]: n Remove directory: /tmp/tar-1.27.1/gnu (358 files)? [YNLQ]: n Remove directory: /tmp/tar-1.27.1/gnu/uniwidth (2 files)? [YNLQ]: n rm -f /tmp/tar-1.27.1/INSTALL Remove directory: /tmp/tar-1.27.1/lib (19 files)? [YNLQ]: ...
Actually I just came upon the -depth option of the find command that is exactly what I was looking for. I can't believe I just missed that: -depth Process each directory's contents before the directory itself. The -delete action also implies -depth. So similar to #Jidder's code, I can write this: dirs=$(find ./test_script -depth -type d); for i in $dirs; do read -p "Delete files in $i? " REPLY; if [[ $REPLY == [yY] ]]; then rm -r $i; fi; done; And for more readability: dirs=$(find ./test_script -depth -type d) for i in $dirs do read -p "Delete files in $i? " REPLY if [[ $REPLY == [yY] ]] then rm -r $i fi done;
Shell script randomly fails to execute file operations commands
I'm having a bit of trouble running this script. Every now and then, it will fail to execute a mv command or rm command referenced below. Then the next iteration will feel the full repercussions of that failure. Why is this happening, and how do I control for it? For reference, the syntax phyml -i $f [parameters] outputs the files $f_phyml_tree.txt and $f_phyml_stats.txt into the same directory as $f. I want to get both these files out of that directory while saving the tree somewhere else. ber="_phyml_tree.txt" for f in ~/randseqs/aa/*.txt; do echo $f fpath=`echo $f | cut -d'/' -f 6` if [ ! -f /home/mcb3421_10/phymlout/aa/$fpath$ber ] || [ ! -f /home/mcb3421_10/phymltimer/aa/$fpath ]; then phyml -i $f -d aa -b 0 -m Blosum62 > ~/blown.txt grep "Time used" < ~/blown.txt > ~/phymltimer/aa/$fpath mv /home/mcb3421_10/randseqs/aa/*$ber phymlout/aa if [ ! -f /home/mcb3421_10/phymlout/aa/$fpath$ber ]; then echo $f failed to write, check the logfile /home/mcb3421_10/phymllogs/aa/$fpath fi rm ~/randseqs/aa/*_stat* mv ~/blown.txt ~/phymllogs/aa/$fpath fi done for f in ~/randseqs/nuc/*.txt; do echo $f fpath=`echo $f | cut -d'/' -f 6` if [ ! -f /home/mcb3421_10/phymlout/nuc/$fpath$ber ] || [ ! -f /home/mcb3421_10/phymltimer/nuc/$fpath ]; then phyml -i $f -d nt -b 0 -m HKY85 > ~/blown.txt grep "Time used" < ~/blown.txt > ~/phymltimer/nuc/$fpath mv /home/mcb3421_10/randseqs/nuc/*$ber phymlout/nuc if [ ! -f /home/mcb3421_10/phymlout/nuc/$fpath$ber ]; then echo $f failed to write, check the logfile /home/mcb3421_10/phymllogs/nuc/$fpath fi rm ~/randseqs/nuc/*_stat* mv ~/blown.txt ~/phymllogs/nuc/$fpath fi done
How to check if a file is empty in Bash?
I have a file called diff.txt. I Want to check whether it is empty. I wrote a bash script something like below, but I couldn't get it work. if [ -s diff.txt ] then touch empty.txt rm full.txt else touch full.txt rm emtpy.txt fi
Misspellings are irritating, aren't they? Check your spelling of empty, but then also try this: #!/bin/bash -e if [ -s diff.txt ]; then # The file is not-empty. rm -f empty.txt touch full.txt else # The file is empty. rm -f full.txt touch empty.txt fi I like shell scripting a lot, but one disadvantage of it is that the shell cannot help you when you misspell, whereas a compiler like your C++ compiler can help you. Notice incidentally that I have swapped the roles of empty.txt and full.txt, as #Matthias suggests.
[ -s file.name ] || echo "file is empty"
[ -s file ] # Checks if file has size greater than 0 [ -s diff.txt ] && echo "file has something" || echo "file is empty" If needed, this checks all the *.txt files in the current directory; and reports all the empty file: for file in *.txt; do if [ ! -s $file ]; then echo $file; fi; done
To check if file is empty or has only white spaces, you can use grep: if [[ -z $(grep '[^[:space:]]' $filename) ]] ; then echo "Empty file" ... fi
While the other answers are correct, using the "-s" option will also show the file is empty even if the file does not exist. By adding this additional check "-f" to see if the file exists first, we ensure the result is correct. if [ -f diff.txt ] then if [ -s diff.txt ] then rm -f empty.txt touch full.txt else rm -f full.txt touch empty.txt fi else echo "File diff.txt does not exist" fi
Easiest way for checking if file is empty or not: if [ -s /path-to-file/filename.txt ] then echo "File is not empty" else echo "File is empty" fi You can also write it on single line: [ -s /path-to-file/filename.txt ] && echo "File is not empty" || echo "File is empty"
#geedoubleya answer is my favorite. However, I do prefer this if [[ -f diff.txt && -s diff.txt ]] then rm -f empty.txt touch full.txt elif [[ -f diff.txt && ! -s diff.txt ]] then rm -f full.txt touch empty.txt else echo "File diff.txt does not exist" fi
[[ -f filename && ! -s filename ]] && echo "filename exists and is empty"
Many of the answers are correct but I feel like they could be more complete / simplistic etc. for example : Example 1 : Basic if statement # BASH4+ example on Linux : typeset read_file="/tmp/some-file.txt" if [ ! -s "${read_file}" ] || [ ! -f "${read_file}" ] ;then echo "Error: file (${read_file}) not found.. " exit 7 fi if $read_file is empty or not there stop the show with exit. More than once I have had misread the top answer here to mean the opposite. Example 2 : As a function # -- Check if file is missing /or empty -- # Globals: None # Arguments: file name # Returns: Bool # -- is_file_empty_or_missing() { [[ ! -f "${1}" || ! -s "${1}" ]] && return 0 || return 1 }
Similar to #noam-manos's grep-based answer, I solved this using cat. For me, -s wasn't working because my "empty" file had >0 bytes. if [[ ! -z $(cat diff.txt) ]] ; then echo "diff.txt is not empty" else echo "diff.txt is empty" fi
I came here looking for how to delete empty __init__.py files as they are implicit in Python 3.3+ and ended up using: find -depth '(' -type f -name __init__.py ')' -print0 | while IFS= read -d '' -r file; do if [[ ! -s $file ]]; then rm $file; fi; done Also (at least in zsh) using $path as the variable also breaks your $PATH env and so it'll break your open shell. Anyway, thought I'd share!