LinuxQuestions.org
Did you know LQ has a Linux Hardware Compatibility List?
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie
User Name
Password
Linux - Newbie This 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

Reply
 
Search this Thread
Old 05-15-2008, 07:20 PM   #1
yao_man
LQ Newbie
 
Registered: May 2008
Posts: 7

Rep: Reputation: 0
Bash shell script question


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.

Last edited by yao_man; 05-15-2008 at 07:22 PM.
 
Old 05-15-2008, 07:50 PM   #2
bjagee
Member
 
Registered: Jan 2007
Location: Portland, OR
Distribution: Ubuntu Jaunty / Fedora
Posts: 39

Rep: Reputation: 15
Can you post the contents of the script? I'm guessing that find is what you are looking for, but I would have to see it in context.
 
Old 05-15-2008, 08:42 PM   #3
pixellany
LQ Veteran
 
Registered: Nov 2005
Location: Annapolis, MD
Distribution: Arch/XFCE
Posts: 17,802

Rep: Reputation: 729Reputation: 729Reputation: 729Reputation: 729Reputation: 729Reputation: 729Reputation: 729
You can use a loop, such as:
Code:
for file in *; do
command
command
.
.
done
In the loop you can use the value of "file", eg:
rm $file

Last edited by pixellany; 05-15-2008 at 08:43 PM.
 
Old 05-15-2008, 09:12 PM   #4
calraith
Member
 
Registered: Apr 2008
Location: Gray, TN, USA
Distribution: UbuntuStudio, Linux Mint
Posts: 36

Rep: Reputation: 15
I'm sure there are less awkward ways to do it, but what I do is as follows:

Code:
for i in `ls -Amp | sed -r -e 's/,//g'`; do {
   command $i
}; done
which will get the dot files as well.
 
Old 05-15-2008, 09:13 PM   #5
calraith
Member
 
Registered: Apr 2008
Location: Gray, TN, USA
Distribution: UbuntuStudio, Linux Mint
Posts: 36

Rep: Reputation: 15
You could also use find with the -maxdepth option, and possibly -type f to exclude directories if you wish.
 
Old 05-15-2008, 10:41 PM   #6
weibullguy
ReliaFree Maintainer
 
Registered: Aug 2004
Location: Kalamazoo, Michigan
Distribution: Slackware-current, Cross Linux from Scratch, Gentoo
Posts: 2,753
Blog Entries: 1

Rep: Reputation: 238Reputation: 238Reputation: 238
Quote:
Originally Posted by yao_man View Post
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.
 
Old 05-15-2008, 11:20 PM   #7
rlhartmann
Member
 
Registered: Mar 2008
Posts: 73

Rep: Reputation: 16
If your files have spaces in the file name, you must quote the
variable name

ls "$FILE" or
rm -f "$FILE"

also, if you wanted to delete files that were more than say five days
old, you could do something like

find $TRASHDIR -type f -mtime +5 -exec rm -f {} \;

# 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.
 
Old 05-16-2008, 12:07 AM   #8
chrism01
Guru
 
Registered: Aug 2004
Location: Sydney
Distribution: Centos 6.6, Centos 5.10
Posts: 16,324

Rep: Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041Reputation: 2041
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...

Last edited by chrism01; 05-16-2008 at 12:36 AM.
 
Old 05-16-2008, 12:41 AM   #9
yao_man
LQ Newbie
 
Registered: May 2008
Posts: 7

Original Poster
Rep: Reputation: 0
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".
 
Old 05-16-2008, 12:45 AM   #10
yao_man
LQ Newbie
 
Registered: May 2008
Posts: 7

Original Poster
Rep: Reputation: 0
I tried "for file in *" and it got all of the files correctly, but it couldn't get the hidden files. Is there a way to include hidden files in this?
 
Old 05-16-2008, 03:23 AM   #11
jschiwal
Guru
 
Registered: Aug 2001
Location: Fargo, ND
Distribution: SuSE AMD64
Posts: 15,733

Rep: Reputation: 655Reputation: 655Reputation: 655Reputation: 655Reputation: 655Reputation: 655
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.
 
Old 05-16-2008, 01:41 PM   #12
yao_man
LQ Newbie
 
Registered: May 2008
Posts: 7

Original Poster
Rep: Reputation: 0
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 View Post
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.

Last edited by yao_man; 05-16-2008 at 02:12 PM.
 
Old 05-16-2008, 06:06 PM   #13
yao_man
LQ Newbie
 
Registered: May 2008
Posts: 7

Original Poster
Rep: Reputation: 0
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.

Last edited by yao_man; 05-16-2008 at 06:08 PM.
 
Old 05-16-2008, 06:22 PM   #14
rlhartmann
Member
 
Registered: Mar 2008
Posts: 73

Rep: Reputation: 16
Sorry, I forgot to mention,

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
Quote:
IFS="
"

for file in `ls -b`
do
....
 
Old 05-16-2008, 06:42 PM   #15
yao_man
LQ Newbie
 
Registered: May 2008
Posts: 7

Original Poster
Rep: Reputation: 0
What is the default value of $IFS?

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.

Last edited by yao_man; 05-16-2008 at 07:18 PM.
 
  


Reply

Tags
bash, directory, file, folder, iterate, shell


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
bash shell script question noisewater Programming 3 11-14-2005 09:39 PM
Bash shell script moodupani Linux - General 2 09-01-2005 07:14 AM
bash shell script globeTrotter Linux - Newbie 5 06-03-2004 06:07 AM
bash/shell script mikis Programming 2 10-14-2003 12:39 PM
bash shell script MaryM Linux - Newbie 0 02-16-2002 12:45 AM


All times are GMT -5. The time now is 11:15 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration