Как удалить часть пути из списка файлов и скопировать ее в другой файл?

Мне нужно выполнить следующие вещи с помощью сценариев bash во FreeBSD:

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

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

2 ответа

Решение

Что я мог сделать, так это взять имена и пути 1000 уникальных файлов с помощью команд "find" и "grep" и поместить их в список

Я собираюсь предположить, что есть файл, который содержит в каждой строке полный путь к каждому файлу (FULL_PATH_TO_LIST_FILE). Учитывая, что с этим процессом не так много статистики, я это пропустил. Вы можете добавить свой собственный однако.

cd WHEREVER_YOU_WANT_TO_CREATE_NEW_FILES
for file_path in `cat FULL_PATH_TO_LIST_FILE`
do
     ## This extracts only the file name from the path
     file_name=`basename $file_path`

     ## This grabs the files size in bytes
     file_size=`wc -c < $file_path`

     ## Create the file and place info regarding original file within new file
     echo -e "$file_name \nThis file is $file_size bytes "> $file_name

done

[Обновление: я хотел вернуться к этому вопросу, чтобы попытаться сделать мой ответ более полезным и переносимым между платформами (OS X - это Unix!) И $SHELLs, хотя в первоначальном вопросе были указаны bash и zsh. Другие ответы предполагали временный список файлов со "случайными" именами файлов, так как вопрос не показал, как был построен список или как был сделан выбор. Я показываю один метод для построения списка в моем ответе, используя временный файл. Я не уверен, как можно рандомизировать find Операция "встроенная" и надеюсь, что кто-то еще может показать, как это может быть сделано (переносимо). Я также надеюсь, что это привлечет некоторые комментарии и критику: вы никогда не сможете узнать слишком много уловок $ SHELL. Я удалил ссылку на Perl, но тем самым заставляю себя сделать это снова в Perl и - потому что Perl довольно переносим - заставить его работать в Windows. Я подожду некоторое время для комментариев, а затем укороту и уберу этот ответ. Спасибо.]

Создание списка файлов

Вы можете многое сделать с помощью GNU find(1). Следующее создаст один файл с именами файлов и тремя разделенными табуляцией столбцами данных, которые вы хотите (имя файла, местоположение, размер в килобайтах).

find / -type f -fprintf tmp.txt '%f\t%h/%f\t%k \n'

Я предполагаю, что вы хотите быть случайным по всем именам файлов (т.е. без ссылок), поэтому вы будете получать записи из всей файловой системы. На моей рабочей станции 800000 файлов, но много оперативной памяти, так что это не займет много времени. У моего ноутбука ~ 300K файлов и не так много памяти, но создание полного списка заняло всего пару минут или около того. Вы хотите настроить, исключив или удалив определенные каталоги из поиска.

Хорошая вещь о -fprintf Флаг в том, что он, кажется, заботится о пробелах в именах файлов. Изучив файл с vim а также sed (т.е. ищет строки с пробелами) и сравнивает вывод wc -l а также uniq Вы можете получить представление о своем выводе и о том, является ли полученный список нормальным или нет. Вы могли бы затем передать это через cut, grep или же sed, awk и друзья для того, чтобы создавать файлы так, как вы хотите. Например, из командной строки:

~/# touch `cat tmp.txt |cut -f1` 
~/# for i in `cat tmp.txt|cut -f1`; do cat tmp.txt | grep $i > $i.dat ; done

Я даю файлы, которые мы создаем .dat расширение, чтобы отличить их от файлов, к которым они относятся, и чтобы их было легче перемещать или удалять, вам не нужно этого делать: просто отключите расширение $i > $i,

Плохая вещь о -fprintf Флаг в том, что он доступен только с GNU find и не является стандартным флагом POSIX, поэтому он не будет доступен в OS X или BSD find(1) (хотя GNU find может быть установлен на ваш Unix как gfind или же gnufind). Более портативный способ сделать это - создать прямой список файлов с find / -type f > tmp.txt (это занимает около 15 секунд в моей системе с 800k-файлами и множеством медленных дисков в пуле ZFS. В комментариях людям будет проще найти что-нибудь более эффективное!). Оттуда вы можете создавать значения данных, которые вы хотите, используя стандартные утилиты для обработки списка файлов, как показано выше в Florin Stingaciu.

#!/bin/sh

# portably get a random number (OS X, BSD, Linux and $SHELLs w/o $RANDOM)
randnum=`od -An -N 4 -D < /dev/urandom` ; echo $randnum


  for file in `cat tmp.txt`
   do
      name=`basename $file`
      size=`wc -c $file |awk '{print $1}'`

# Uncomment the next line to see the values on STDOUT 
#      printf "Location: $name \nSize: $size \n"

# Uncomment the next line to put data into the respective .dat files 
#      printf "Location: $file \nSize: $size \n" > $name.dat

 done

# vim: ft=sh

Если вы следите за этим, вы поймете, что это создаст много файлов - на моей рабочей станции это создаст 800 тыс. .dat файлы, которые не то, что мы хотим! Итак, как случайным образом выбрать 1000 файлов из нашего списка 800k для обработки? Есть несколько способов сделать это.

Случайный выбор из списка файлов

У нас есть список всех файлов в системе (!). Теперь, чтобы выбрать 1000 файлов, нам просто нужно случайным образом выбрать 1000 строк из нашего файла листинга (tmp.txt). Мы можем установить верхний предел номера строки, чтобы выбрать путем генерации случайного числа, используя прохладный od Техника, которую вы видели выше - она ​​настолько крутая и кроссплатформенная, что у меня есть это псевдоним в моей оболочке;-) - затем выполняется деление по модулю %) на нем, используя количество строк в файле в качестве делителя. Затем мы просто берем это число и выбираем строку в файле, которой оно соответствует с помощью awk или sed (например, sed -n <$RANDOMNUMBER>p filelist), итерация 1000 раз и Presto! У нас есть новый список из 1000 случайных файлов. Или нет... это действительно медленно! Ища способ ускорить awk а также sed Я наткнулся на отличный трюк, используя dd от Alex Lines, который ищет файл байтами (вместо строк) и переводит результат в строку, используя sed или же awk, Смотрите блог Алекса для деталей. Мои единственные проблемы с его техникой пришли с настройкой count= переключиться на достаточно высокий номер. По таинственным причинам (которые, я надеюсь, кто-то объяснит) - возможно, потому что мой locale является LC_ALL=en_US.UTF-8 - dd будет плевать неполные строки в randlist.txt если я не установлю count= намного большее число, чем фактическая максимальная длина линии. Я думаю, что я, вероятно, смешивал символы и байты. Есть объяснения?

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

#!/bin/sh
IFS='
'                                                                                
# We create tmp.txt with                                                        
# find / -type f > tmp.txt  # tweak as needed.                                  
#                                                                               
files="tmp.txt"                                                           

# Get the number of lines and maximum line length for later                                                                              
bytesize=`wc -c < $files`                                                 
# wc -L is not POSIX and we need to multiply so:
linelenx10=`awk '{if(length > x) {x=length; y = $0} }END{print x*10}' $files`

# A function to generate a random number modulo the                             
# number of bytes in the file. We'll use this to find a                         
# random location in our file where we can grab a line                          
# using dd and sed. 

genrand () {                                                                    
  echo `od -An -N 4 -D < /dev/urandom` ' % ' $bytesize | bc                     
}                                                                               

rm -f randlist.txt                                                             

i=1                                                                             
while [ $i -le 1000 ]                                                          
do                             
 # This probably works but is way too slow: sed -n `genrand`p $files                
 # Instead, use Alex Lines' dd seek method:
 dd if=$files skip=`genrand` ibs=1 count=$linelenx10 2>/dev/null |awk 'NR==2 {print;exit}'>> randlist.txt

 true $((i=i+1))    # Bourne shell equivalent of $i++ iteration    
done  

for file in `cat randlist.txt`                                                 
  do                                                                           
   name=`basename $file`                                                        
   size=`wc -c <"$file"`                                 
   echo -e "Location: $file \n\n Size: $size" > $name.dat  
  done    

# vim: ft=sh 
Другие вопросы по тегам