Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have the misfortune of coming from a MS-DOS background - but at least it makes me appreciate how much more powerful Linux is. I've been working on getting my Linux-Fu up to par, but there are a couple things that could be done with DOS that I'm not sure how to accomplish most easily with Linux:

Renaming Multiple Files - Using Two Wildcards

c:\> dir

Directory of c:\
    file1.txt
    file2.txt
    file3.txt
    file4.txt

c:\>rename *.txt *.bak

c:\> dir

Directory of c:\
    file1.bak
    file2.bak
    file3.bak
    file4.bak

I know I could use find -exec here but it it possible to use a shorter syntax - perhaps mv with some special flags or syntax? I guess the key to this is the second * wildcard as linux shouldn't have a problem with the first one (i.e. i know how to select the files i want to rename using wildcards)

Renaming a Single File - Using One Wildcard

c:\> dir

Directory of c:\
    file1.txt

c:\>rename file1.txt *.bak

c:\> dir

Directory of c:\
    file1.bak

This would be especially helpful when renaming long and unwieldy file names. I thought perhaps I could use mv file1.txt $1.bak to end up with file1.txt.bak which would also be acceptable but I'm not sure you can reference a the $1 parameter inline with a shell command. Again in this particular case it is just convenient of how ms-dos bastardizes the * wildcard to be used as a sort of capture / replace match for part of the filename.

Filtering Directory Listings with a Wildcard

c:\> dir

Directory of c:\
    file1.txt
    file2.txt
    file3.txt
    file4.txt
    text.txt
    \temp       (directory) 

c:\> dir file*

Directory of c:\
    file1.txt
    file2.txt
    file3.txt
    file4.txt

c:\> t*

Directory of c:\
    text.txt
    \temp       (directory) 

I'm not sure what the right syntax for doing that with ls is, or if it is even possible. If I did something like ls t* it will recurse into directories starting with t. My workaround has either been using find . --max-depth 1 -iname "t*" or something like ls -al | grep t - neither of which are as short and simple as dir t* is.

Finally, I know I can set up aliases to make these long commands shorter, but I'd like to learn some out-of-the-box linux-fu for doing these things because sometimes you're connected to a remote system or working on a new machine.

So how can I mv and ls files the same way that I used to dir and rename files?

share|improve this question
add comment

3 Answers

up vote 9 down vote accepted

One of the fundamental differences between Windows cmd and POSIX shells is who is responsible for wildcard expansions. Shells do all the expansions required before starting the actual commands you asked for. cmd mostly passes the wildcard patterns to the commands unmodified. (I say mostly, since I think there are exceptions, and environment variables are expanded under most circumstances.) This makes writing a rename that would work with the same syntax as in cmd quite tricky.

But there is a rename for Linux - with completely different arguments, check out the man page (which is a bit terse on my system, and rename comes from the util-linux package on my system, which should be widely available). Your first rename would be done like this:

rename .txt .bak *.txt

Note that the shell does the * expansion, so rename itself actually thinks it was invoked like this:

rename .txt .bak file1.txt file2.txt file3.txt ...

So you can guess the single file version:

rename .txt .bak file1.txt

If you don't want to use rename but implement this yourself, you could create a function for that. Assuming you only want to change the file extension, and for single-file rename, look at this:

$ function chext() {
  newext=$1
  file=$2
  newfile=${file%.*}$newext
  echo mv $file $newfile
}
$ chext .csv test.txt
mv text.txt text.csv

$newfile is built using a substring removal to strip out the original extension, then concatenates the new extension. You can extend that function to handle multiple files relatively easily.


As for your ls question, use the -d switch. This will prevent ls from listing the contents of directories.

Demo:

$ ls -al
total 536
drwx------   3 owner users 528384 Jan  7 17:29 .
drwxr-xr-x 126 owner users  12288 Jan  7 17:26 ..
-rw-r--r--   1 owner users      0 Jan  7 17:28 f1.csv
-rw-r--r--   1 owner users      0 Jan  7 17:28 f2.csv
-rw-r--r--   1 owner users      0 Jan  7 17:28 f3.csv
-rw-r--r--   1 owner users      0 Jan  7 17:28 f4.csv
drwxr-xr-x   2 owner users   4096 Jan  7 17:33 test
-rw-r--r--   1 owner users      0 Jan  7 17:27 test.csv

Wildcard rename

$ rename .csv .txt f*
$ ls -al
total 536
drwx------   3 owner users 528384 Jan  7 17:34 .
drwxr-xr-x 126 owner users  12288 Jan  7 17:26 ..
-rw-r--r--   1 owner users      0 Jan  7 17:28 f1.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f2.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f3.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f4.txt
drwxr-xr-x   2 owner users   4096 Jan  7 17:33 test
-rw-r--r--   1 owner users      0 Jan  7 17:27 test.csv

Single-file rename

$ rename .txt .csv f1.txt 
$ ls -al
total 536
drwx------   3 owner users 528384 Jan  7 17:34 .
drwxr-xr-x 126 owner users  12288 Jan  7 17:26 ..
-rw-r--r--   1 owner users      0 Jan  7 17:28 f1.csv
-rw-r--r--   1 owner users      0 Jan  7 17:28 f2.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f3.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f4.txt
drwxr-xr-x   2 owner users   4096 Jan  7 17:33 test
-rw-r--r--   1 owner users      0 Jan  7 17:27 test.csv

The default ls

$ ls -l t*
-rw-r--r-- 1 owner users    0 Jan  7 17:27 test.csv

test:
total 0
-rw-r--r-- 1 owner users 0 Jan  7 17:33 dont_show_me_please

ls that doesn't inspect directories

$ ls -ld t*
drwxr-xr-x 2 owner users 4096 Jan  7 17:33 test
-rw-r--r-- 1 owner users    0 Jan  7 17:27 test.csv
share|improve this answer
    
very nice! i think i've just moved up a belt with my linux-fu! thanks! –  cwd Jan 7 '12 at 17:15
    
Tip to progress a bit with this (and avoid pitfalls): use simple functions or scripts to see what happens when you do somefunc *.Ext if that pattern doesn't match any file, and play with quoting to see if you manage to pass a pattern around (without having it expanded) between functions. Safety tip: don't use rm anywhere while experimenting with globbing/shell expansion :-) –  Mat Jan 7 '12 at 17:24
add comment

One thing to keep in mind when it comes to wildcards is that they're expanded by the shell. The application doesn't know whether you used wildcards or typed the names out. For example, if you type rename *.txt *.bak, then the rename command sees something like rename file1.txt file2.txt existingfile.bak. That's not enough information to go on.

I'll deal with the question about ls first, because it's simpler. If all you want is the matching names, then you don't need ls, because the shell is already doing the expansion.

echo t*

If you want more information about the files, then pass the -d option to ls, to tell it not to list the contents of directories.

ls -ld t*

There is no standard utility for renaming files, because the first unix systems didn't come with one. The portable method to rename files uses a loop and is a little verbose:

for x in *.txt; do mv -- "$x" "${x%.txt}.bak"; done

There are several common utilities to rename files, none of which are guaranteed to be installed on a given unix system, but all of which are easy to install. Here are the main ones:

  • rename from the util-linux suite, available on every non-embedded Linux system (and nowhere else). On Debian and derivatives (including Ubuntu), this command is called rename.ul. Provided that there is no occurrence of .txt other than the final extension, you can write

    rename .txt .bak *.txt
    
  • rename is a Perl script that Debian and derivatives ship as /usr/bin/rename. You can rename files according to arbitrary Perl commands.

    rename 's/\.txt\z/\.bak/' *.txt
    
  • mmv, which can rename, copy and link files according to several name-based patterns and has many options relating to what happens if a target name already exists. Note that you must use quotes to protect wildcards from expansion by the shell.

    mmv '*.txt' '#1.txt'
    
  • zmv is a zsh function, available if and only if your shell is zsh. It can match arbitrary zsh patterns (so you can match file names according to arbitrary regular expressions, not just wildcards, and you can match files by other criteria such as dates and sizes). zmv can also copy and link.

    zmv '(*).txt' '$1.txt'
    

If you have some control over the machines you use, my recommendation is to use zsh as your shell (it has other benefits over bash) and put these lines in your ~/.zshrc:

autoload -U zmv
alias zmv='noglob zmv -w'
alias zcp='zmv -C'
alias zln='zmv -L'
alias zsy='zmv -Ls'

noglob is a zsh feature that tells the shell not to expand wildcards in the argument of the command. This way you can write zmv *.txt \$1.txt (you'll always need to protect the $ in a replacement text).

share|improve this answer
add comment

Yes, it works very well. Just be cautious with using mv command ;-)

mv behaviour is not what you would expect to be same on *Unix/Linux environment.

Just search for using wildcards in bash shell in google.

share|improve this answer
    
a few examples would be nice - and yes, i realize that mv behavior is different - which is why i'm asking the question. –  cwd Jan 7 '12 at 16:10
    
ok, here you go: tuxfiles.org/linuxhelp/wildcards.html –  Nikhil Mulley Jan 7 '12 at 16:12
    
I'm going to have to give you a -1 because you don't seem to have taken time to understand my question. I just checked your wildcards page and I don't see any examples where two wildcards are used, like in the fashion mv *.txt *.bak... –  cwd Jan 7 '12 at 16:14
    
I am fine with -1 but I am sorry that I perhaps did not answer you what you wanted me to. Let me attempt it again. wildcards is a feature of shell, really, that helps to operate on group of files. The wildcard characters are expanded in shell before they are given to the program to be executed. Its not specific to ls or mv, however they are designed to support the shell wildcard expansion. BTW, mv *.txt *.bak ?? ---- Did you execute this command in your shell before? What was the result like? –  Nikhil Mulley Jan 7 '12 at 16:29
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.