Вот полный Vagrantfile
, который устанавливает Oh My Zsh на коробку Ubuntu 14.04.2 LTS и устанавливает его в качестве оболочки по умолчанию для стандартного пользователя vagrant
.
Это работает с Vagrant 1.7.2. (Ваш учет может варьироваться в зависимости от версии.) Вместо того, чтобы пытаться использовать автоматические сценарии, он использует указания из раздела Установка вручную в файле Readme.
# -*- mode: ruby -*-
# vi: set ft=ruby :
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# Pick a box to use:
config.vm.box = "ubuntu/trusty64"
############################################################
# Oh My ZSH Install section
# Install git and zsh prerequisites
config.vm.provision :shell, inline: "apt-get -y install git"
config.vm.provision :shell, inline: "apt-get -y install zsh"
# Clone Oh My Zsh from the git repo
config.vm.provision :shell, privileged: false,
inline: "git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh"
# Copy in the default .zshrc config file
config.vm.provision :shell, privileged: false,
inline: "cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc"
# Change the vagrant user's shell to use zsh
config.vm.provision :shell, inline: "chsh -s /bin/zsh vagrant"
############################################################
end
В качестве бонуса вы можете сделать однократную копию файла .zshrc
вашего хост-компьютера в поле для бродяги с:
config.vm.provision "file", source: "~/.zshrc", destination: ".zshrc"
(Имейте в виду, вам может потребоваться вещи, которые изначально не работают из-за различий между хост-машиной и настройками бродячей коробки.)
Как сказал АльбертоПЛ, сохраняйте строки в формате FIFO для последующего использования - не «возвращайтесь назад». Для этого я бы определенно использовал python вместо bash + sed / awk / что угодно.
Мне потребовалось несколько минут, чтобы написать этот фрагмент кода:
from collections import deque
line_fifo = deque()
for line in open("test"):
line_fifo.append(line)
if len(line_fifo) == 4:
# "look 3 lines backward"
if line_fifo[0] == line_fifo[-1] == "@STRING_A\n":
# get rid of that match
line_fifo.popleft()
else:
# print out the top of the fifo
print line_fifo.popleft(),
# don't forget to print out the fifo when the file ends
for line in line_fifo: print line,
Конечно, Python тоже будет работать. Просто сохраните последние три строки в массиве и проверьте, совпадает ли первый элемент в массиве со значением, которое вы в данный момент читаете. Затем удалите значение и распечатайте текущий массив. Затем вы переместите свои элементы, чтобы освободить место для нового значения, и повторите. Конечно, когда массив заполнен, вам нужно будет убедиться, что вы продолжаете перемещать значения из массива и вставлять новые прочитанные значения, останавливаясь каждый раз, чтобы проверять, соответствует ли первое значение в массиве значению, которое вы в настоящее время читаю.
Почему это невозможно в bash? Вам не нужно хранить в памяти весь файл, только последние три строки (если я правильно понял) и писать то, что соответствует стандарту. Перенаправьте это во временный файл, убедитесь, что все работает должным образом, и перезапишите исходный файл временным.
То же самое и для Python.
Я бы предоставил собственный сценарий, но это не так. быть протестированным. ; -)
Этот код просканирует файл и удалит строки, начинающиеся с маркера. По умолчанию он хранит в памяти только три строки:
from collections import deque
def delete(fp, marker, gap=3):
"""Delete lines from *fp* if they with *marker* and are followed
by another line starting with *marker* *gap* lines after.
"""
buf = deque()
for line in fp:
if len(buf) < gap:
buf.append(line)
else:
old = buf.popleft()
if not (line.startswith(marker) and old.startswith(marker)):
yield old
buf.append(line)
for line in buf:
yield line
Я тестировал его с помощью:
>>> from StringIO import StringIO
>>> fp = StringIO('''a
... b
... xxx 1
... c
... xxx 2
... d
... e
... xxx 3
... f
... g
... h
... xxx 4
... i''')
>>> print ''.join(delete(fp, 'xxx'))
a
b
xxx 1
c
d
e
xxx 3
f
g
h
xxx 4
i
Я бы подумал об использовании sed. gnu sed поддерживает определение диапазонов строк. если sed не удастся, тогда есть еще один зверь - awk, и я уверен, что вы можете сделать это с awk.
Хорошо, я чувствую, что мне нужно поставить свой awk POC. Я не мог понять, как использовать адреса sed. Я не пробовал комбинацию awk + sed, но мне кажется, что это излишне.
мой сценарий awk работает следующим образом:
Он считывает строки и сохраняет их в 3-строчном буфере
после того, как найден желаемый шаблон ( /^data.*/ в моем случае), 3-строчный буфер просматривается, чтобы проверить, был ли виден желаемый шаблон три строки назад
, если шаблон был замечен, то 3 строки царапаются
, чтобы быть честно говоря, я бы, наверное, тоже выбрал python, учитывая, что awk действительно неудобен. код AWK следующий:
function max(a, b) { if (a > b) return a; else return b; } BEGIN { w = 0; #write index r = 0; #read index buf[0, 1, 2]; #buffer } END { # flush buffer # start at read index and print out up to w index for (k = r % 3; k r - max(r - 3, 0); k--) { #search in 3 line history buf if (match(buf[k % 3], /^data.*/) != 0) { # found -> remove lines from history # by rewriting them -> adjust write index w -= max(r, 3); } } buf[w % 3] = $0; w++; } /^.*/ { # store line into buffer, if the history # is full, print out the oldest one. if (w > 2) { print buf[r % 3]; r++; buf[w % 3] = $0; } else { buf[w] = $0; } w++; }
В bash вы можете использовать sort -r filename
и tail -n filename
для чтения файла в обратном направлении.
$LINES=`tail -n filename | sort -r`
# now iterate through the lines and do your checking
Забавно, что по прошествии стольких часов никто еще не дал решение проблемы, как на самом деле сформулировано (как @John Machin указывает в комментарии) - удалите только ведущий маркер (если за ним следует другой такой маркер на 3 строки вниз), а не всю строку, содержащую его. Это, конечно, несложно - вот небольшой мод, необходимый для забавного решения @ truppo, например:
from itertools import izip, chain
f = "foo.txt"
for third, line in izip(chain(" ", open(f)), open(f)):
if third.startswith("@STRING_A") and line.startswith("@STRING_A"):
line = line[len("@STRING_A"):]
print line,
Конечно, в реальной жизни можно использовать iterator.tee
вместо чтения файл дважды, используйте этот код в функции, а не повторяйте константу маркера бесконечно, & c; -).
Вот более интересное решение, использующее два итератора с трехэлементным смещением :)
from itertools import izip, chain, tee
f1, f2 = tee(open("foo.txt"))
for third, line in izip(chain(" ", f1), f2):
if not (third.startswith("@STRING_A") and line.startswith("@STRING_A")):
print line,
Этот «ответ» для лиры ... Я исправлю свой предыдущий комментарий: если стрелка находится в первых 3 строках файла, ваш сценарий либо вызовет ошибку IndexError, либо обратится к строке, которой он не должен получить доступ, иногда с интересными побочными эффектами.
Пример вашего скрипта, вызывающего IndexError:
>>> lines = "@string line 0\nblah blah\n".splitlines(True)
>>> needle = "@string "
>>> for i,line in enumerate(lines):
... if line.startswith(needle) and lines[i-3].startswith(needle):
... lines[i-3] = lines[i-3].replace(needle, "")
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
IndexError: list index out of range
, и этот пример показывает не только то, что Земля круглая, но также и то, почему ваше «исправление» к «не удалять целиком» строка "проблема должна была использовать .replace (игла," ", 1)
или [len (игла):]
вместо .replace (игла," ")
>>> lines = "NEEDLE x NEEDLE y\nnoddle\nnuddle\n".splitlines(True)
>>> needle = "NEEDLE"
>>> # Expected result: no change to the file
... for i,line in enumerate(lines):
... if line.startswith(needle) and lines[i-3].startswith(needle):
... lines[i-3] = lines[i-3].replace(needle, "")
...
>>> print ''.join(lines)
x y <<<=== whoops!
noddle
nuddle
<<<=== still got unwanted newline in here
>>>
Взгляните на mapstraction . Это может дать вам больше гибкости при предоставлении карт на основе google, osm, yahoo и т. Д., Однако ваш код не придется менять.
(мой код выше удаляет всю строку, а не только флаг @STRING_A)Это легко исправить, изменив команду на sed:
sed `awk 'BEGIN{ORS=";"}
/@STRING_A/ {
if(LAST!="" && LAST+3 >= NR) print LAST "s/@STRING_A//"
LAST = NR
}' test_file` test_file
Это может быть то, что вы ищете?
lines = open('sample.txt').readlines()
needle = "@string "
for i,line in enumerate(lines):
if line.startswith(needle) and lines[i-3].startswith(needle):
lines[i-3] = lines[i-3].replace(needle, "")
print ''.join(lines)
это выводит:
string 0 extra text
string 1 extra text
string 2 extra text
string 3 extra text
--replaced -- 4 extra text
string 5 extra text
string 6 extra text
@string 7 extra text
string 8 extra text
string 9 extra text
string 10 extra text