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 a text file called CAMS.txt that contains the following:

4153999999999991
4153999999999992
4153999999999993
4153999999999994
4801999999999991
4801999999999992
4801999999999993

I would like to split the CAMS.txt file into 2 files - CAMS1.txt and CAMS2.txt. Their contents are as follows

CAMS1.txt

4153999999999991
4153999999999992
4153999999999993
4153999999999994

CAMS2.txt

4801999999999991
4801999999999992
4801999999999993

It's really splitting the file based on the first 4 digits of the original CAMS.txt file. It will always be 4153 and 4801. I'm new to the unix world =)

share|improve this question

3 Answers 3

awk '/^4153/ {print >"CAMS1.TXT"; next} {print >"CAMS2.TXT"}' CAMS.TXT

There are other ways to do that, another would be using two grep commands

grep "^4153" CAMS.TXT > CAMS1.TXT
grep -v "^4153" CAMS.TXT > CAMS2.TXT

That's less efficient but easier to type, after the first grep is done, you recall it from your shell history (using the "up" arrow key) and makes a few changes. Of course the file is read two times, so don't do that if it is huge.

share|improve this answer

For this particular case where you know the text already, you can do something like

while read line; do 
    [[ $line =~ ^4153 ]] && 
        printf "%s\n" "$line" >> CAMS1.TXT || 
        printf "%s\n" "$line" >> CAMS2.TXT 
done < CAMS.TXT 

This reads each line of CAMS.TXT into the variable $line and then, if $line starts with 4153, it prints to CAMS1 and if not, it prints to CAMS2.

Alternatively, you could print each line to standard error or standard output depending on what the first numbers are and redirect the command's output accordingly. For example:

perl -ne '/^4153/ ? print STDOUT : print STDERR' CAMS.TXT >CAMS1.TXT 2>CAMS2.TXT 

If you don't know what the text will be, you could just write each line to a file whose name is the first 4 characters of the line:

awk '{print >> substr($1,1,4)}' CAMS.TXT 

The above will create two files, 4153 and 4801 each containing the lines you expect. This has the advantage of working with any number of different patterns.

share|improve this answer

Here's a pure bash variant on terdon's more general solution.

while read line; do 
  echo "$line" >> "${line:0:4}.txt"
done < CAMS.txt

The resulting files will be named according to the first four characters on each line in them, e.g. 4153.txt and 4801.txt for the sample input.

The following snippet can be used to batch rename the resulting files to CAMS1.txt, CAMS2.txt etc. (assuming the first four characters of each line in the original input were in fact digits).

i=1
for file in [0-9][0-9][0-9][0-9].txt; do
  mv "$file" "CAMS$(( i++ )).txt"
done

This relies on the observation that numbers in the original input file are in increasing order, and the file numbering of the sample output files correspond to this ordering.

Explanation:

while read line; do 
  ...
done < CAMS.txt

Iterates the fileCAMS.txt, reading each line in turn into the variable line.

echo "$line" >> "${line:0:4}.txt"

Appends the line currently being processed to a file, the name of which is obtained from the first four characters of the current line.


i=1

Assign the value 1 to the variable i.

for file in [0-9][0-9][0-9][0-9].txt; do
  ...
done

Iterate files in the current directory which have four digits in their name and end in the .txt extension. The [0-9][0-9][0-9][0-9].txt in the above snippet is called a shell glob. This feature can be used to match or expand specific types of patterns, in this case file names.

mv "$file" "CAMS$(( i++ )).txt"

Rename the file currently being processed. The destination file name, "CAMS$(( i++ )).txt", consists of the prefix CAMS concatenated with the current value of the variable i. The variable i is at the same time incremented within a bash arithmetic expression, as indicated by the (( ... )) syntax, using the postfix increment operator ++. The additional $ before (( ... )) causes the value of the expression, in this case the value of i before the increment, to be expanded into the string indicating the destination file name. Lastly, the suffix .txt is appended to the destination file name.

share|improve this answer

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.