21

My directory structure is as follows

Directory1\file1.jpg \file2.jpg \file3.jpg Directory2\anotherfile1.jpg \anotherfile2.jpg \anotherfile3.jpg Directory3\yetanotherfile1.jpg \yetanotherfile2.jpg \yetanotherfile3.jpg 

I'm trying to use the command line in a bash shell on ubuntu to take the first file from each directory and rename it to the directory name and move it up one level so it sits alongside the directory.

In the above example:

  • file1.jpg would be renamed to Directory1.jpg and placed alongside the folder Directory1

  • anotherfile1.jpg would be renamed to Directory2.jpg and placed alongside the folder Directory2

  • yetanotherfile1.jpg would be renamed to Directory3.jpg and placed alongside the folder Directory3

I've tried using:

find . -name "*.jpg" 

but it does not list the files in sequential order (I need the first file).

This line:

find . -name "*.jpg" -type f -exec ls "{}" +; 

lists the files in the correct order but how do I pick just the first file in each directory and move it up one level?

Any help would be appreciated!

Edit: When I refer to the first file what I mean is each jpg is numbered from 0 to however many files in that folder - for example: file1, file2...... file34, file35 etc... Another thing to mention is the format of the files is random, so the numbering might start at 0 or 1a or 1b etc...

1
  • Expanding on Ignacio's comment, it's important to know what you mean by "first". First alphabetically? If so and if the numbers go to 2 digits, do you want to sort file3.jpg ahead of file10.jpg instead of the alphabetical way where file10.jpg would come first? If you don't want alphabetically, do you want the first in order of the way their stored on disk? By date? And so on. Commented Jan 24, 2013 at 20:25

4 Answers 4

36

You can go inside each dir and run:

$ mv `ls | head -n 1` .. 
Sign up to request clarification or add additional context in comments.

3 Comments

simple and Unixy, I like
Hi @LưuVĩnhPhúc, thanks for the link. The article makes a good point. In my case however, the filenames were all auto-generated and following a very specific predefined format. So the edge cases the article warns about don't really apply. In general cases, yes, probably not a good idea. Thanks!
30

If first means whatever the shell glob finds first (lexical, but probably affected by LC_COLLATE), then this should work:

for dir in */; do for file in "$dir"*.jpg; do echo mv "$file" "${file%/*}.jpg" # If it does what you want, remove the echo break 1 done done 

Proof of concept:

$ mkdir dir{1,2,3} && touch dir{1,2,3}/file{1,2,3}.jpg $ for dir in */; do for file in "$dir"*.jpg; do echo mv "$file" "${file%/*}.jpg"; break 1; done; done mv dir1/file1.jpg dir1.jpg mv dir2/file1.jpg dir2.jpg mv dir3/file1.jpg dir3.jpg 

Comments

2

Look for all first level directories, identify first file in this directory and then move it one level up

find . -type d \! -name . -prune | while read d; do f=$(ls $d | head -1) mv $d/$f . done 

Comments

0

Building on the top answer, here is a general use bash function that simply returns the first path that resolves to a file within the given directory:

getFirstFile() { for dir in "$1"; do for file in "$dir"*; do if [ -f "$file" ]; then echo "$file" break 1 fi done done } 

Usage:

# don't forget the trailing slash getFirstFile ~/documents/ 

NOTE: it will silently return nothing if you pass it an invalid path.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.