Сценарий оболочки - список файлов, чтение файлов и запись данных в новый файл

У меня есть особый вопрос по написанию сценариев оболочки.
Простое создание сценариев для меня не проблема, но я новичок в этом деле и хочу создать простой файл базы данных.

Итак, что я хочу сделать, это:

- Search for filetypes (i.e. .nfo) <-- should be no problem :)
- read inside of each found file and use some strings inside
- these string of each file should be written in a new file. Each found file informations 

должна быть одна строка в новом файле

Надеюсь, я хорошо объяснил свой «проект».

Теперь моя проблема заключается в том, чтобы понять, как я могу сказать сценарию, что он должен искать файлы, а затем использовать каждый из этих файлов для чтения в нем и использовать некоторую информацию в нем, чтобы записать это в новый файл.

Я объясню немного лучше.
Я ищу файлы, и это возвращает меня:

файл1.nfo
файл2.nfo
файл3.nfo

Хорошо, теперь в каждом из этих файлов мне нужна информация между двумя строками. то есть
file1.nfo:

<user>test1</user>

file2.nfo:

<user>test2</user>

поэтому в новом файле теперь должно быть:

file1.nfo:user1
file2.nfo:user2

OK so:

find -name *.nfo  > /test/database.txt

распечатывает список файлов. и

sed -n '/<user*/,/<\/user>/p' file1.nfo

возвращает мне весь файл, а не только информацию между <user> и </user>

Я стараюсь идти шаг за шагом и много читаю, но это кажется очень трудным.

Что я делаю неправильно и как лучше всего составить список всех файлов и записать файлы и содержимое между двумя строками в файл?

ИЗМЕНИТЬ-НОВОЕ:

Хорошо, вот обновление для получения дополнительной информации. Теперь я многому научился и искал в Интернете свои проблемы. Я могу найти много информации, но я не знаю, как собрать ее вместе, чтобы я мог ее использовать.

Теперь работа с awk заключается в том, что я возвращаю имя файла и строку.

Вот теперь полная информация (я думал, что смогу продолжить сам с небольшой помощью, но я не могу :( )

Вот пример: /test/file1.nfo

<string1>STRING 1</string1>
<string2>STRING 2</string2>
<string3>STRING 3</string3>
<string4>STRING 4</string4>
<personal informations>
<hobby>Baseball</hobby>
<hobby>Baskeball</hobby>
</personal informations>

Вот пример /test/file2.nof

<string1>STRING 1</string1>
<string2>STRING 2</string2>
<string3>STRING 3</string3>
<string4>STRING 4</string4>
<personal informations>
<hobby>Soccer</hobby>
<hobby>Traveling</hobby>
</personal informations>

Файл, который я хочу создать, должен выглядеть так.

STRING 1:::/test/file1.nfo:::Date of file:::STRING 4:::STRING 3:::Baseball, Basketball:::STRING 2
STRING 1:::/test/file2.nfo:::Date of file:::STRING 4:::STRING 3:::Baseball, Basketball:::STRING 2

«Дата файла» должна быть датой создания файла. Чтобы я мог видеть, сколько лет файлу.

Итак, это то, что мне нужно, и это кажется непростым.

Большое спасибо.

ОШИБКА ОБНОВЛЕНИЯ -printf

find: unrecognized: -printf

Usage: find [PATH]... [OPTIONS] [ACTIONS]

Search for files and perform actions on them.
First failed action stops processing of current file.
Defaults: PATH is current directory, action is '-print'

    -follow         Follow symlinks
    -xdev           Don't descend directories on other filesystems
    -maxdepth N     Descend at most N levels. -maxdepth 0 applies
                    actions to command line arguments only
    -mindepth N     Don't act on first N levels
    -depth          Act on directory *after* traversing it

Actions:
    ( ACTIONS )     Group actions for -o / -a
    ! ACT           Invert ACT's success/failure
    ACT1 [-a] ACT2  If ACT1 fails, stop, else do ACT2
    ACT1 -o ACT2    If ACT1 succeeds, stop, else do ACT2
                    Note: -a has higher priority than -o
    -name PATTERN   Match file name (w/o directory name) to PATTERN
    -iname PATTERN  Case insensitive -name
    -path PATTERN   Match path to PATTERN
    -ipath PATTERN  Case insensitive -path
    -regex PATTERN  Match path to regex PATTERN
    -type X         File type is X (one of: f,d,l,b,c,...)
    -perm MASK      At least one mask bit (+MASK), all bits (-MASK),
                    or exactly MASK bits are set in file's mode
    -mtime DAYS     mtime is greater than (+N), less than (-N),
                    or exactly N days in the past
    -mmin MINS      mtime is greater than (+N), less than (-N),
                    or exactly N minutes in the past
    -newer FILE     mtime is more recent than FILE's
    -inum N         File has inode number N
    -user NAME/ID   File is owned by given user
    -group NAME/ID  File is owned by given group
    -size N[bck]    File size is N (c:bytes,k:kbytes,b:512 bytes(def.))
                    +/-N: file size is bigger/smaller than N
    -links N        Number of links is greater than (+N), less than (-N),
                    or exactly N
    -prune          If current file is directory, don't descend into it
If none of the following actions is specified, -print is assumed
    -print          Print file name
    -print0         Print file name, NUL terminated
    -exec CMD ARG ; Run CMD with all instances of {} replaced by
                    file name. Fails if CMD exits with nonzero
    -delete         Delete current file/directory. Turns on -depth option

person Thomas    schedule 04.04.2013    source источник
comment
UNIX просто не хранит даты создания файлов, поэтому вы можете получить дату/время создания файла, только если у вас есть какой-либо другой инструмент, который записывает эту информацию при создании файла. Есть ли какая-то другая дата, которая вас интересует (например, дата последнего изменения)?   -  person Ed Morton    schedule 05.04.2013
comment
Да дата модификации тоже очень хороша, она должна быть лучше создания. или дата последней копии или что-то в этом роде.   -  person Thomas    schedule 05.04.2013


Ответы (3)


Все, что тебе нужно:

find -name '*.nfo' | xargs awk -F'[><]' '{print FILENAME,$3}'

Если в вашем файле есть больше, чем просто то, что вы показываете в своем образце ввода, то это, вероятно, все, что вам нужно:

... awk -F'[><]' '/<user>/{print FILENAME,$3}' file

Попробуйте это (не проверено):

> outfile
find -name '*.nfo' -printf "%p %Tc\n" |
while IFS= read -r fname tstamp
do
      awk -v tstamp="$tstamp" -F'[><]' -v OFS=":::" '
          { a[$2] = a[$2] sep[$2] $3; sep[$2] = ", " }
          END {
              print a["string1"], FILENAME, tstamp, a["string4"], a["string3"], a["hobby"], a["string2"]
          }
      ' "$fname" >> outfile
done

Вышеприведенное будет работать только в том случае, если ваши имена файлов не содержат пробелов. Если они могут, нам нужно настроить цикл.

Альтернатива, если ваша находка не поддерживает -printf (предложение - серьезно подумайте о том, чтобы получить современную «находку»!):

> outfile
find -name '*.nfo' -print |
while IFS= read -r fname
do
      tstamp=$(stat -c"%x" "$fname")
      awk -v tstamp="$tstamp" -F'[><]' -v OFS=":::" '
          { a[$2] = a[$2] sep[$2] $3; sep[$2] = ", " }
          END {
              print a["string1"], FILENAME, tstamp, a["string4"], a["string3"], a["hobby"], a["string2"]
          }
      ' "$fname" >> outfile
done

Если у вас нет «статистики», поищите в Google альтернативы для получения метки времени из файла или рассмотрите возможность анализа вывода ls -l - это ненадежно, но если это все, что у вас есть...

person Ed Morton    schedule 04.04.2013
comment
Спасибо, это тоже хорошо работает. Но ‹текста› внутри файла больше. Мне нужно выбрать некоторые и распечатать его в файл. Но awk вроде хорош, посмотрю документацию. - person Thomas; 05.04.2013
comment
Я изменил сценарий, исходя из предположений о том, что могут содержать ваши входные файлы — если вы обновите образец ввода в своем вопросе, чтобы он был более репрезентативным для вашего реального ввода, я покажу вам, как это сделать в awk. - person Ed Morton; 05.04.2013
comment
Хорошо, ваше редактирование теперь очень хорошо. Я думаю, что опубликую в своем первом посте, что именно я ищу. Но я уже много узнал о find, grep и теперь awk :) - person Thomas; 05.04.2013
comment
@ Томас, не могли бы вы попробовать еще раз? Ваш вопрос теперь совершенно неясен и запутан. Теперь вы показываете файл с некоторыми тегами, а затем другие файлы с кучей двоеточий с некоторым текстом между тегами в произвольном порядке, и некоторым текстом PATH/FILENAME, который появляется из ниоткуда, и т. д. Просто покажите пара примеров входных файлов и вывод, который вы хотите получить от них, с описанием того, почему это должен быть вывод. - person Ed Morton; 05.04.2013
comment
Я разместил обновленное решение. Обратите внимание, что вы НЕ МОЖЕТЕ получить время создания файла из UNIX, поэтому вместо этого я использую время последней модификации в своем ответе. - person Ed Morton; 05.04.2013
comment
Ух ты. Я бы никогда не пришел к такому решению :) кажется, что printf в моей системе невозможен: find: uncognized: -printf - person Thomas; 05.04.2013
comment
Исправил ошибку в первом посте. Может есть альтернатива -printf? - person Thomas; 05.04.2013
comment
Альтернативой получению временной метки из поиска является изменение find printf на печать, а затем получение временной метки после поиска путем запуска выбранного вами инструмента на $fnmame в цикле перед вызовом awk. Я изменю свой ответ, чтобы показать вам, что я имею в виду. - person Ed Morton; 05.04.2013
comment
Да, вы правы, стат невозможен. Но я постараюсь найти решение здесь. Без метки времени он работает абсолютно идеально. Удивительно! Большое спасибо. - person Thomas; 05.04.2013
comment
Обновлять. Я только что установил статистику :-) - person Thomas; 05.04.2013
comment
Отлично, теперь иди, установи, найди :-). Если вы довольны этим решением, не забудьте поставить галочку рядом с ним, чтобы другие не тратили свое время, пытаясь помочь вам найти решение, когда оно у вас уже есть. - person Ed Morton; 05.04.2013
comment
Хорошо, я сделаю это. find уже установлен, но printf не работает. но решение со стат работает. Не тот формат времени, который мне нужен, но я найду решение для этого. Другой вопрос. Если есть баллы. в имени файла это не работает. Сообщение: найти: непризнанный: this.is.a.test.3.nfo - person Thomas; 05.04.2013
comment
попробуйте процитировать RE, это '*.nfo', а не просто *.nfo. Я обновлю свой ответ. - person Ed Morton; 05.04.2013
comment
Это может быть так просто. :-) Спасибо, это решение. пробовал не ок. Основы теперь в порядке, и я отмечу вопрос. Большое спасибо, теперь мне нужно поискать в Интернете другие проблемы и, возможно, задать новый вопрос. Еще раз большое спасибо. - person Thomas; 05.04.2013

Обозначение pat1,pat2 sed основано на строках. Думайте об этом так: pat1 устанавливает флаг включения для своих команд, а pat2 отключает флаг. Если и pat1, и pat2 находятся в одной строке, флаг будет установлен, и, таким образом, в вашем случае выведите все, что следует за строкой <user>, включая ее. Дополнительную информацию см. в инструкции grymoire по sed.

Альтернативой sed в этом случае было бы использование grep, который поддерживает проверки вокруг, например. GNU-группа:

find . -type f -name '*.nfo' | xargs grep -oP '(?<=<user>).*(?=</user>)'

Если grep не поддерживает -P, вы можете использовать комбинацию grep и sed:

find . -type f -name '*.nfo' | xargs grep -o '<user>.*</user>' | sed 's:</\?user>::g'

Выход:

./file1.nfo:test1
./file2.nfo:test2

Обратите внимание: вы должны знать о проблемах, связанных с передачей файлов на xargs, и, возможно, использовать -exec ... вместо этого. .

person Thor    schedule 04.04.2013
comment
Спасибо. Но похоже, что функция -P здесь не реализована в моей среде: grep: недопустимая опция -- 'P' - person Thomas; 04.04.2013
comment
найти . -type f -name '*.nfo' | xargs grep -o '‹пользователь›.*‹/пользователь›' | sed 's:‹/\?user›::g' ---- Это работает. Я возвращаю пользователя. теперь я должен поиграть с ним. Большое тебе спасибо - person Thomas; 04.04.2013
comment
Но это возвращает мне только test1, а не имя фильма. мне также нужно еще несколько частей внутри файлов nfo. но я буду играть и искать здесь. Я уверен, что найду некоторые решения, которые они уже опубликовали. Спасибо - person Thomas; 04.04.2013
comment
Обновите снова. Он работает с путем/именем файла и содержимым. Спасибо еще раз - person Thomas; 04.04.2013

Бывает так, что grep выводит в нужном вам формате и его достаточно для однострочного .

По умолчанию grep '' *.nfo выводит что-то вроде:

file1.nfo:random data  
file1.nfo:<user>test1</user>  
file1.nfo:some more random data  
file2.nfo:not needed  
file2.nfo:<user>test2</user>  
file2.nfo:etc etc  

Добавив параметр -P (Perl RegEx), вы можете ограничить вывод только совпадениями:

grep -P "<user>\w+<\/user>" *.nfo

выход:

file1.nfo:<user>test1</user>  
file2.nfo:<user>test2</user>  

Теперь опция -o (показывать только то, что совпало) спасает положение, но нам понадобится немного более продвинутый RegEx, так как теги не нужны:

grep -oP "(?<=<user>)\w+(?=<\/user>)" *.nfo > /test/database.txt

вывод cat /test/database.txt:

file1.nfo:test1 
file2.nfo:test2  

Объяснение RegEx здесь: http://regex101.com/r/oU2wQ1

И весь ваш скрипт стал одной командой.

Обновление:

Если у вас нет опции --perl-regexp, попробуйте:

grep -oE "<user>\w+<\/user>" *.nfo|sed 's#</?user>##g' > /test/database.txt
person CSᵠ    schedule 04.04.2013
comment
ОК Большое спасибо вам обоим. - person Thomas; 04.04.2013
comment
-P и --perl-regexp являются синонимами. Вы можете использовать их взаимозаменяемо. - person rioki; 04.04.2013
comment
\w — это аббревиатура, которую понимают только некоторые инструменты. Попробуйте эквивалент POSIX [[:alnum:]_] - person Ed Morton; 05.04.2013