Linux - NewbieThis Linux forum is for members that are new to Linux.
Just starting out and have a question?
If it is not in the man pages or the how-to's this is the place!
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
If this isn't the right place I apologize, but it seemed appropriate to me. I have been playing around with my shell script configuration, and I remapped rm and rmdir to move files to the trash instead and I created a delete function that can be used to delete files or directories after checking for confirmation a few times. Now the question, is there a way to iterate over all of the items in a directory. I tried ls -b and ls -1 but both have problems when the filename includes a space. BTW, this is all located in an alias file included in the ~/.bashrc. The alias file is called ~/.bash_addon if that makes any difference. Thank you for any help.
both have problems when the filename includes a space.
When filenames include spaces you need to wrap the name in single quotes. For example
Code:
ls -b 'test file 1.txt'
If you use a loops or other recursive methods that use a variable you may find that commands inside the loop don't work on the files with spaces. Try using the find command with the -exec option. For example
Code:
find ./ -maxdepth 1 -type f -exec ls -b '{}' \; -exec file '{}' \;
Will find every file of type file in the current directory and then execute the ls -b and file command on the found file You can add more than two commands.
# The find command above will remove all files (files only -type f) more than five days old
out of the directory with name stored in the variable $TRASHDIR.
Although <space> is a valid char in a filename etc most cmds also use <space> as argument separators, so its highly advisable (for a quiet life) for you to avoid using spaces in filenames...
I had at this point just run a test script to try and access all the file names and print them.
Code:
for file in `ls -b`;do
if [ -d "$file" ];then
echo "\"$file\" is a directory"
elif [ -f "$file" ];then
echo "\"$file\" is a file"
else
echo "\"$file\" is something else"
fi
done
I ran it on a test directory and it returned all of them until the file "test 1" which was read as "test/" and "1".
You could either use
for file in $(ls -A)
or
for file in $(find . -maxdepth 1 -type f)
The latter will return only files.
Rather than aliasing rm and rmdir you might want to name the command something like "trash" so that the name describes what you want to do. You might also might want to only "trash" the files that you give in arguments instead of all of the contents in the current directory.
I aliased rm and rmdir so that they move the files to the trash instead of removing them completely by using a "trash" function I wrote. I am trying to create a delete function that checks for approval before completely removing the given file and anything within that file (even if it is a directory) so I can easily remove files and directories when I need to.
Quote:
Originally Posted by jschiwal
You could either use
for file in $(ls -A)
or
for file in $(find . -maxdepth 1 -type f)
Both of those have troubles if the file has a space in it.
BTW, this is more of an attempt to teach myself programming problem solving and logic as well as overall programming ability then an attempt at efficiency.
I apologize for this uber long post, but here goes. Ok, I did further searching and found a lot of useful information. I got the function almost working perfectly. The only problem is it will only erase hidden or non-hidden files in one go. If you switch block1 and block2 (they are labeled in the program) it switches which is deleted on the first go and which is deleted on the second go. I commented out two lines so that I could read what the program would execute at completion in the file "delete_tmp" Here is my code
Code:
delete () {
if [ "$del_number" == "0" ];then
dir_list=""
fi
disp_string=""
disp_arg=""
no_exist=""
for arg in $*;do
if [[ "${arg:0:1}" != "/" ]];then
arg="$PWD/$arg"
fi
if [[ "${arg:0:${#HOME}}" == "$HOME" ]];then
disp_arg="~${arg:${#HOME}}"
else
disp_arg="$arg"
fi
if [ -d "$arg" ];then
disp_string+="$disp_arg/*\n"
elif [ -r "$arg" ];then
disp_string+="$disp_arg\n"
else
no_exist+="\n$disp_arg"
fi
done
if [[ $1 && $disp_string && "${1:0:2}" != "-h" && "${1:0:6}" != "--help" ]];then
if [[ ! $del_number > 0 ]];then
echo -e "The following files and all contents will be deleted:\n${disp_string:0:(${#disp_string}-2)}\nOnce completed, action cannot be reversed"
read -sn 1 -p "Continue? [y,n]" ANS
echo
if [[ "$ANS" == "y" || "$ANS" == "Y" ]];then
echo -e "All contents will be permanently lost\nAction cannot be reversed"
read -sn 1 -p "Are you sure? [y,n]" ANS
echo
fi
else
ANS="y"
fi
if [[ "$ANS" == "y" || "$ANS" == "Y" ]];then
del_number=$((del_number + 1))
for arg in $*;do
if [ -r "$arg" ];then
if [ -d "$arg" ];then
dir_list="/bin/rmdir \"$arg\"\n$dir_list"
if [ $(ls -A "$arg" | wc -l) != "0" ];then
#start of block 1
for file in $arg/.*;do
if [[ ${file:(${#file}-1):1} == "." || ${file:(${#file}-2):2} == ".." ]];then
continue
elif [ -d "$file" ];then
delete "$file"
elif [ -r "$file" ];then
echo "/bin/rm \"$file\"" >> delete_tmp
fi
done
#end of block1
#start of block2
for file in $arg/*;do
if [ -d "$file" ];then
delete "$file"
elif [ -r "$file" ];then
echo "/bin/rm \"$file\"" >> delete_tmp
fi
done
#end of block2
fi
elif [ -r "$arg" ];then
echo "/bin/rm \"$arg\"" >> delete_tmp
fi
else
if [[ "${arg:0:1}" != "/" ]];then
arg="$PWD/$arg"
fi
if [[ "${arg:0:${#HOME}}" == "$HOME" ]];then
arg="~${arg:${#HOME}}"
fi
echo "\"$arg\" could not be accessed or does not exist"
fi
done
del_number=$((del_number - 1))
if [ "$del_number" == "0" ];then
if [ "$dir_list" ];then
echo -e "${dir_list:0:(${#dir_list}-2)}" >> delete_tmp
dir_list=""
fi
del_number=""
#next 2 lines uncommented for diagnosis
# cat delete_tmp | bash
# /bin/rm delete_tmp
fi
else
echo -e "Deletion aborted"
fi
elif [[ "${1:0:2}" == "-h" || "${1:0:6}" == "--help" ]];then
echo -e "del[ete] [-h/--help/file...]\n -h/--help\tDisplay this help\n -file...\tCompletely and permanently remove file[s] and contents\n\t\tfrom the system (once removed they cannot be retrieved)"
else
echo -e "None of the files exist or could be accessed\nDeletion cancelled"
fi
if [ $no_exist ];then
echo -e "The following files did not exist or could not be accessed:$no_exist"
fi
}
Here is a little breakdown of things in the code:
if -h or --help is the first option, display the help instead of running the delete function call
$del_number is used to tell if this is the initial delete call or if it is a delete call from inside a delete call
$disp_string is used when displaying the arguments offered for deletion
$ANS is used to check for confirmation for deletion on the first call and ignored on any calls from inside that same initial call
...further checks for empty directories ($(ls -A "$arg" | wc -l) != "0") etc
#block1 arranges for deletion of files starting with "." excluding "." and ".."
#block2 arranges for deletion of non hidden files
$del_number check sees if it is the initial call, add the directory deletion info to the end of "delete_tmp", then run "delete_tmp", then delete "delete_tmp"
If block1 is first, it deletes hidden files on the first call and the rest on the second call. If block2 is first, it is the other way around. I have some sample output available if you need it, but this post is too long already. Just ask to see it. Also, it still has problems if an argument contains spaces. e.g. delete 'test 1' tries to delete 'test' and '1' and I don't know how to fix that either. Thank you for any help you may offer. (code optimizations would be nice as well.
If you are doing a for loop, you MUST redefine
the internal separate field using a command like
IFS="
"
The command above is QUOTING the return key,
type IFS=" then hit the enter key, no spaces,
then the quote, this is critcal for file names
for spaces using your for loop
Nevermind, I think I got it. As for the program, still experiencing problems. Using IFS, I have it set up right, but somewhere in the loop, $arg gets lost so it doesn't see the files correctly.
Nevermind again...I switched back to my way, but I need to find out how to keep $arg from getting lost in the first for loop in block1.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.