Merge remote-tracking branch 'LCTT/master'

This commit is contained in:
Xingyu Wang 2019-05-23 09:28:33 +08:00
commit dc0452881e
8 changed files with 988 additions and 1004 deletions

View File

@ -1,32 +1,32 @@
[#]: collector: (lujun9972)
[#]: translator: (geekpi)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: reviewer: (wxy)
[#]: publisher: (wxy)
[#]: url: (https://linux.cn/article-10885-1.html)
[#]: subject: (Building Smaller Container Images)
[#]: via: (https://fedoramagazine.org/building-smaller-container-images/)
[#]: author: (Muayyad Alsadi https://fedoramagazine.org/author/alsadi/)
构建更小的容器镜像
构建更小的容器镜像的技巧
======
![][1]
Linux 容器已经成为一个热门话题,保证容器镜像较小被认为是一个好习惯。本文提供了有关如何构建较小 Fedora 容器镜像的一些提示
Linux 容器已经成为一个热门话题,保证容器镜像较小被认为是一个好习惯。本文提供了有关如何构建较小 Fedora 容器镜像的一些技巧
### microdnf
Fedora 的 DNF 是用 Python 编写的,因为它有各种各样的插件,因此它的设计是可扩展的。但是 Fedora 有一个替代的基本容器镜像,它使用一个名为 [microdnf][2] 的较小的包管理器,使用 C 编写。要在 Dockerfile 中使用这个最小的镜像FROM 行应该如下所示:
Fedora 的 DNF 是用 Python 编写的,因为它有各种各样的插件,因此它的设计是可扩展的。但是 有一个 Fedora 基本容器镜像替代品,它使用一个较小的名为 [microdnf][2] 的包管理器,使用 C 编写。要在 Dockerfile 中使用这个最小的镜像,`FROM` 行应该如下所示:
```
FROM registry.fedoraproject.org/fedora-minimal:30
```
如果你的镜像不需要像 Python 这样的典型 DNF 依赖项,那么这是一个重要的节省项。例如,如果你在制作 NodeJS 镜像。
如果你的镜像不需要像 Python 这样的典型 DNF 依赖项,例如,如果你在制作 NodeJS 镜像时,那么这是一个重要的节省项
### 在一个层中安装和清理
为了节省空间,使用 _dnf clean all_ 或其 microdnf 等效的 _microdnf clean all_ 删除仓库元数据非常重要。但是你不应该分两步执行此操作,因为这实际上会将这些文件保存在容器镜像中,然后在另一层中将其标记为删除。要正确地执行此操作,你应该像这样一步完成安装和清理:
为了节省空间,使用 `dnf clean all` 或其 microdnf 等效的 `microdnf clean all` 删除仓库元数据非常重要。但是你不应该分两步执行此操作,因为这实际上会将这些文件保存在容器镜像中,然后在另一层中将其标记为删除。要正确地执行此操作,你应该像这样一步完成安装和清理:
```
FROM registry.fedoraproject.org/fedora-minimal:30
@ -35,21 +35,21 @@ RUN microdnf install nodejs && microdnf clean all
### 使用 microdnf 进行模块化
模块化是一种给你选择堆栈不同版本的方法。例如,你可能需要在项目中用非 LTS 的 NodeJS v11旧的 LTS NodeJS v8 用于另一个,最新的 LTS NodeJS v10 用于另一个。你可以使用冒号指定流。
模块化是一种给你选择不同堆栈版本的方法。例如,你可能需要在项目中用非 LTS 的 NodeJS v11旧的 LTS NodeJS v8 用于另一个,最新的 LTS NodeJS v10 用于另一个。你可以使用冒号指定流。
```
# dnf module list
# dnf module install nodejs:8
```
_dnf module install_ 命令意味着两个命令,一个启用流,另一个是从它安装 nodejs。
`dnf module install` 命令意味着两个命令,一个启用流,另一个是从它安装 nodejs。
```
# dnf module enable nodejs:8
# dnf install nodejs
```
尽管 microdnf 不提供与模块化相关的任何命令,但是可以启用有配置文件的模块,并且 libdnf被 microdnf 使用)[似乎][3]支持模块化流。该文件看起来像这样:
尽管 `microdnf` 不提供与模块化相关的任何命令,但是可以启用有配置文件的模块,并且 libdnf被 microdnf 使用)[似乎][3]支持模块化流。该文件看起来像这样:
```
/etc/dnf/modules.d/nodejs.module
@ -60,7 +60,7 @@ profiles=
state=enabled
```
使用模块化的 microdnf 的完整 Dockerfile 如下所示:
使用模块化的 `microdnf` 的完整 Dockerfile 如下所示:
```
FROM registry.fedoraproject.org/fedora-minimal:30
@ -89,9 +89,9 @@ COPY --from=build /go/bin/confd /usr/local/bin
CMD ["confd"]
```
通过在 _FROM_ 指令之后添加 _AS_ 并从基本容器镜像中添加另一个 _FROM_ 然后使用 _COPY --from=_ 指令将内容从_构建_的容器复制到第二个容器来完成多阶段构建。
通过在 `FROM` 指令之后添加 `AS` 并从基本容器镜像中添加另一个 `FROM` 然后使用 `COPY --from=` 指令将内容从*构建*的容器复制到第二个容器来完成多阶段构建。
可以使用 podman 构建并运行此 Dockerfile
可以使用 `podman` 构建并运行此 Dockerfile
```
$ podman build -t myconfd .
@ -105,7 +105,7 @@ via: https://fedoramagazine.org/building-smaller-container-images/
作者:[Muayyad Alsadi][a]
选题:[lujun9972][b]
译者:[geekpi](https://github.com/geekpi)
校对:[校对者ID](https://github.com/校对者ID)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出

View File

@ -1,5 +1,5 @@
[#]: collector: (lujun9972)
[#]: translator: (sanfusu)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )

View File

@ -1,735 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (MjSeven)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (API evolution the right way)
[#]: via: (https://opensource.com/article/19/5/api-evolution-right-way)
[#]: author: (A. Jesse https://opensource.com/users/emptysquare)
API evolution the right way
======
Ten covenants that responsible library authors keep with their users.
![Browser of things][1]
Imagine you are a creator deity, designing a body for a creature. In your benevolence, you wish for the creature to evolve over time: first, because it must respond to changes in its environment, and second, because your wisdom grows and you think of better designs for the beast. It shouldn't remain in the same body forever!
![Serpents][2]
The creature, however, might be relying on features of its present anatomy. You can't add wings or change its scales without warning. It needs an orderly process to adapt its lifestyle to its new body. How can you, as a responsible designer in charge of this creature's natural history, gently coax it toward ever greater improvements?
It's the same for responsible library maintainers. We keep our promises to the people who depend on our code: we release bugfixes and useful new features. We sometimes delete features if that's beneficial for the library's future. We continue to innovate, but we don't break the code of people who use our library. How can we fulfill all those goals at once?
### Add useful features
Your library shouldn't stay the same for eternity: you should add features that make your library better for your users. For example, if you have a Reptile class and it would be useful to have wings for flying, go for it.
```
class Reptile:
@property
def teeth(self):
return 'sharp fangs'
# If wings are useful, add them!
@property
def wings(self):
return 'majestic wings'
```
But beware, features come with risk. Consider the following feature in the Python standard library, and see what went wrong with it.
```
bool(datetime.time(9, 30)) == True
bool(datetime.time(0, 0)) == False
```
This is peculiar: converting any time object to a boolean yields True, except for midnight. (Worse, the rules for timezone-aware times are even stranger.)
I've been writing Python for more than a decade but I didn't discover this rule until last week. What kind of bugs can this odd behavior cause in users' code?
Consider a calendar application with a function that creates events. If an event has an end time, the function requires it to also have a start time.
```
def create_event(day,
start_time=None,
end_time=None):
if end_time and not start_time:
raise ValueError("Can't pass end_time without start_time")
# The coven meets from midnight until 4am.
create_event(datetime.date.today(),
datetime.time(0, 0),
datetime.time(4, 0))
```
Unfortunately for witches, an event starting at midnight fails this validation. A careful programmer who knows about the quirk at midnight can write this function correctly, of course.
```
def create_event(day,
start_time=None,
end_time=None):
if end_time is not None and start_time is None:
raise ValueError("Can't pass end_time without start_time")
```
But this subtlety is worrisome. If a library creator wanted to make an API that bites users, a "feature" like the boolean conversion of midnight works nicely.
![Man being chased by an alligator][3]
The responsible creator's goal, however, is to make your library easy to use correctly.
This feature was written by Tim Peters when he first made the datetime module in 2002. Even founding Pythonistas like Tim make mistakes. [The quirk was removed][4], and all times are True now.
```
# Python 3.5 and later.
bool(datetime.time(9, 30)) == True
bool(datetime.time(0, 0)) == True
```
Programmers who didn't know about the oddity of midnight are saved from obscure bugs, but it makes me nervous to think about any code that relies on the weird old behavior and didn't notice the change. It would have been better if this bad feature were never implemented at all. This leads us to the first promise of any library maintainer:
#### First covenant: Avoid bad features
The most painful change to make is when you have to delete a feature. One way to avoid bad features is to add few features in general! Make no public method, class, function, or property without a good reason. Thus:
#### Second covenant: Minimize features
Features are like children: conceived in a moment of passion, they must be supported for years. Don't do anything silly just because you can. Don't add feathers to a snake!
![Serpents with and without feathers][5]
But of course, there are plenty of occasions when users need something from your library that it does not yet offer. How do you choose the right feature to give them? Here's another cautionary tale.
### A cautionary tale from asyncio
As you may know, when you call a coroutine function, it returns a coroutine object:
```
async def my_coroutine():
pass
print(my_coroutine())
[/code] [code]`<coroutine object my_coroutine at 0x10bfcbac8>`
```
Your code must "await" this object to run the coroutine. It's easy to forget this, so asyncio's developers wanted a "debug mode" that catches this mistake. Whenever a coroutine is destroyed without being awaited, the debug mode prints a warning with a traceback to the line where it was created.
When Yury Selivanov implemented the debug mode, he added as its foundation a "coroutine wrapper" feature. The wrapper is a function that takes in a coroutine and returns anything at all. Yury used it to install the warning logic on each coroutine, but someone else could use it to turn coroutines into the string "hi!"
```
import sys
def my_wrapper(coro):
return 'hi!'
sys.set_coroutine_wrapper(my_wrapper)
async def my_coroutine():
pass
print(my_coroutine())
[/code] [code]`hi!`
```
That is one hell of a customization. It changes the very meaning of "async." Calling set_coroutine_wrapper once will globally and permanently change all coroutine functions. It is, [as Nathaniel Smith wrote][6], "a problematic API" that is prone to misuse and had to be removed. The asyncio developers could have avoided the pain of deleting the feature if they'd better shaped it to its purpose. Responsible creators must keep this in mind:
#### Third covenant: Keep features narrow
Luckily, Yury had the good judgment to mark this feature provisional, so asyncio users knew not to rely on it. Nathaniel was free to replace **set_coroutine_wrapper** with a narrower feature that only customized the traceback depth.
```
import sys
sys.set_coroutine_origin_tracking_depth(2)
async def my_coroutine():
pass
print(my_coroutine())
[/code] [code]
<coroutine object my_coroutine at 0x10bfcbac8>
RuntimeWarning:'my_coroutine' was never awaited
Coroutine created at (most recent call last)
File "script.py", line 8, in <module>
print(my_coroutine())
```
This is much better. There's no more global setting that can change coroutines' type, so asyncio users need not code as defensively. Deities should all be as farsighted as Yury.
#### Fourth covenant: Mark experimental features "provisional"
If you have merely a hunch that your creature wants horns and a quadruple-forked tongue, introduce the features but mark them "provisional."
![Serpent with horns][7]
You might discover that the horns are extraneous but the quadruple-forked tongue is useful after all. In the next release of your library, you can delete the former and mark the latter official.
### Deleting features
No matter how wisely we guide our creature's evolution, there may come a time when it's best to delete an official feature. For example, you might have created a lizard, and now you choose to delete its legs. Perhaps you want to transform this awkward creature into a sleek and modern python.
![Lizard transformed to snake][8]
There are two main reasons to delete features. First, you might discover a feature was a bad idea, through user feedback or your own growing wisdom. That was the case with the quirky behavior of midnight. Or, the feature might have been well-adapted to your library's environment at first, but the ecology changes. Perhaps another deity invents mammals. Your creature wants to squeeze into the mammals' little burrows and eat the tasty mammal filling, so it has to lose its legs.
![A mouse][9]
Similarly, the Python standard library deletes features in response to changes in the language itself. Consider asyncio's Lock. It has been awaitable ever since "await" was added as a keyword:
```
lock = asyncio.Lock()
async def critical_section():
await lock
try:
print('holding lock')
finally:
lock.release()
```
But now, we can do "async with lock."
```
lock = asyncio.Lock()
async def critical_section():
async with lock:
print('holding lock')
```
The new style is much better! It's short and less prone to mistakes in a big function with other try-except blocks. Since "there should be one and preferably only one obvious way to do it," [the old syntax is deprecated in Python 3.7][10] and it will be banned soon.
It's inevitable that ecological change will have this effect on your code, too, so learn to delete features gently. Before you do so, consider the cost or benefit of deleting it. Responsible maintainers are reluctant to make their users change a large amount of their code or change their logic. (Remember how painful it was when Python 3 removed the "u" string prefix, before it was added back.) If the code changes are mechanical, however, like a simple search-and-replace, or if the feature is dangerous, it may be worth deleting.
#### Whether to delete a feature
![Balance scales][11]
Con | Pro
---|---
Code must change | Change is mechanical
Logic must change | Feature is dangerous
In the case of our hungry lizard, we decide to delete its legs so it can slither into a mouse's hole and eat it. How do we go about this? We could just delete the **walk** method, changing code from this:
```
class Reptile:
def walk(self):
print('step step step')
```
to this:
```
class Reptile:
def slither(self):
print('slide slide slide')
```
That's not a good idea; the creature is accustomed to walking! Or, in terms of a library, your users have code that relies on the existing method. When they upgrade to the latest version of your library, their code will break.
```
# User's code. Oops!
Reptile.walk()
```
Therefore, responsible creators make this promise:
#### Fifth covenant: Delete features gently
There are a few steps involved in deleting a feature gently. Starting with a lizard that walks with its legs, you first add the new method, "slither." Next, deprecate the old method.
```
import warnings
class Reptile:
def walk(self):
warnings.warn(
"walk is deprecated, use slither",
DeprecationWarning, stacklevel=2)
print('step step step')
def slither(self):
print('slide slide slide')
```
The Python warnings module is quite powerful. By default it prints warnings to stderr, only once per code location, but you can silence warnings or turn them into exceptions, among other options.
As soon as you add this warning to your library, PyCharm and other IDEs render the deprecated method with a strikethrough. Users know right away that the method is due for deletion.
`Reptile().walk()`
What happens when they run their code with the upgraded library?
```
$ python3 script.py
DeprecationWarning: walk is deprecated, use slither
script.py:14: Reptile().walk()
step step step
```
By default, they see a warning on stderr, but the script succeeds and prints "step step step." The warning's traceback shows what line of the user's code must be fixed. (That's what the "stacklevel" argument does: it shows the call site that users need to change, not the line in your library where the warning is generated.) Notice that the error message is instructive, it describes what a library user must do to migrate to the new version.
Your users will want to test their code and prove they call no deprecated library methods. Warnings alone won't make unit tests fail, but exceptions will. Python has a command-line option to turn deprecation warnings into exceptions.
```
> python3 -Werror::DeprecationWarning script.py
Traceback (most recent call last):
File "script.py", line 14, in <module>
Reptile().walk()
File "script.py", line 8, in walk
DeprecationWarning, stacklevel=2)
DeprecationWarning: walk is deprecated, use slither
```
Now, "step step step" is not printed, because the script terminates with an error.
So, once you've released a version of your library that warns about the deprecated "walk" method, you can delete it safely in the next release. Right?
Consider what your library's users might have in their projects' requirements.
```
# User's requirements.txt has a dependency on the reptile package.
reptile
```
The next time they deploy their code, they'll install the latest version of your library. If they haven't yet handled all deprecations, then their code will break, because it still depends on "walk." You need to be gentler than this. There are three more promises you must keep to your users: maintain a changelog, choose a version scheme, and write an upgrade guide.
#### Sixth covenant: Maintain a changelog
Your library must have a changelog; its main purpose is to announce when a feature that your users rely on is deprecated or deleted.
#### Changes in Version 1.1
**New features**
* New function Reptile.slither()
**Deprecations**
* Reptile.walk() is deprecated and will be removed in version 2.0, use slither()
---
Responsible creators use version numbers to express how a library has changed so users can make informed decisions about upgrading. A "version scheme" is a language for communicating the pace of change.
#### Seventh covenant: Choose a version scheme
There are two schemes in widespread use, [semantic versioning][12] and time-based versioning. I recommend semantic versioning for nearly any library. The Python flavor thereof is defined in [PEP 440][13], and tools like **pip** understand semantic version numbers.
If you choose semantic versioning for your library, you can delete its legs gently with version numbers like:
> 1.0: First "stable" release, with walk()
> 1.1: Add slither(), deprecate walk()
> 2.0: Delete walk()
Your users should depend on a range of your library's versions, like so:
```
# User's requirements.txt.
reptile>=1,<2
```
This allows them to upgrade automatically within a major release, receiving bugfixes and potentially raising some deprecation warnings, but not upgrading to the _next_ major release and risking a change that breaks their code.
If you follow time-based versioning, your releases might be numbered thus:
> 2017.06.0: A release in June 2017
> 2018.11.0: Add slither(), deprecate walk()
> 2019.04.0: Delete walk()
And users can depend on your library like:
```
# User's requirements.txt for time-based version.
reptile==2018.11.*
```
This is terrific, but how do your users know your versioning scheme and how to test their code for deprecations? You have to advise them how to upgrade.
#### Eighth covenant: Write an upgrade guide
Here's how a responsible library creator might guide users:
#### Upgrading to 2.0
**Migrate from Deprecated APIs**
See the changelog for deprecated features.
**Enable Deprecation Warnings**
Upgrade to 1.1 and test your code with:
`python -Werror::DeprecationWarning`
Now it's safe to upgrade.
---
You must teach users how to handle deprecation warnings by showing them the command line options. Not all Python programmers know this—I certainly have to look up the syntax each time. And take note, you must _release_ a version that prints warnings from each deprecated API so users can test with that version before upgrading again. In this example, version 1.1 is the bridge release. It allows your users to rewrite their code incrementally, fixing each deprecation warning separately until they have entirely migrated to the latest API. They can test changes to their code and changes in your library, independently from each other, and isolate the cause of bugs.
If you chose semantic versioning, this transitional period lasts until the next major release, from 1.x to 2.0, or from 2.x to 3.0, and so on. The gentle way to delete a creature's legs is to give it at least one version in which to adjust its lifestyle. Don't remove the legs all at once!
![A skink][14]
Version numbers, deprecation warnings, the changelog, and the upgrade guide work together to gently evolve your library without breaking the covenant with your users. The [Twisted project's Compatibility Policy][15] explains this beautifully:
> "The First One's Always Free"
>
> Any application which runs without warnings may be upgraded one minor version of Twisted.
>
> In other words, any application which runs its tests without triggering any warnings from Twisted should be able to have its Twisted version upgraded at least once with no ill effects except the possible production of new warnings.
Now, we creator deities have gained the wisdom and power to add features by adding methods and to delete them gently. We can also add features by adding parameters, but this brings a new level of difficulty. Are you ready?
### Adding parameters
Imagine that you just gave your snake-like creature a pair of wings. Now you must allow it the choice whether to move by slithering or flying. Currently its "move" function takes one parameter.
```
# Your library code.
def move(direction):
print(f'slither {direction}')
# A user's application.
move('north')
```
You want to add a "mode" parameter, but this breaks your users' code if they upgrade, because they pass only one argument.
```
# Your library code.
def move(direction, mode):
assert mode in ('slither', 'fly')
print(f'{mode} {direction}')
# A user's application. Error!
move('north')
```
A truly wise creator promises not to break users' code this way.
#### Ninth covenant: Add parameters compatibly
To keep this covenant, add each new parameter with a default value that preserves the original behavior.
```
# Your library code.
def move(direction, mode='slither'):
assert mode in ('slither', 'fly')
print(f'{mode} {direction}')
# A user's application.
move('north')
```
Over time, parameters are the natural history of your function's evolution. They're listed oldest first, each with a default value. Library users can pass keyword arguments to opt into specific new behaviors and accept the defaults for all others.
```
# Your library code.
def move(direction,
mode='slither',
turbo=False,
extra_sinuous=False,
hail_lyft=False):
# ...
# A user's application.
move('north', extra_sinuous=True)
```
There is a danger, however, that a user might write code like this:
```
# A user's application, poorly-written.
move('north', 'slither', False, True)
```
What happens if, in the next major version of your library, you get rid of one of the parameters, like "turbo"?
```
# Your library code, next major version. "turbo" is deleted.
def move(direction,
mode='slither',
extra_sinuous=False,
hail_lyft=False):
# ...
# A user's application, poorly-written.
move('north', 'slither', False, True)
```
The user's code still compiles, and this is a bad thing. The code stopped moving extra-sinuously and started hailing a Lyft, which was not the intention. I trust that you can predict what I'll say next: Deleting a parameter requires several steps. First, of course, deprecate the "turbo" parameter. I like a technique like this one, which detects whether any user's code relies on this parameter.
```
# Your library code.
_turbo_default = object()
def move(direction,
mode='slither',
turbo=_turbo_default,
extra_sinuous=False,
hail_lyft=False):
if turbo is not _turbo_default:
warnings.warn(
"'turbo' is deprecated",
DeprecationWarning,
stacklevel=2)
else:
# The old default.
turbo = False
```
But your users might not notice the warning. Warnings are not very loud: they can be suppressed or lost in log files. Users might heedlessly upgrade to the next major version of your library, the version that deletes "turbo." Their code will run without error and silently do the wrong thing! As the Zen of Python says, "Errors should never pass silently." Indeed, reptiles hear poorly, so you must correct them very loudly when they make mistakes.
![Woman riding an alligator][16]
The best way to protect your users is with Python 3's star syntax, which requires callers to pass keyword arguments.
```
# Your library code.
# All arguments after "*" must be passed by keyword.
def move(direction,
*,
mode='slither',
turbo=False,
extra_sinuous=False,
hail_lyft=False):
# ...
# A user's application, poorly-written.
# Error! Can't use positional args, keyword args required.
move('north', 'slither', False, True)
```
With the star in place, this is the only syntax allowed:
```
# A user's application.
move('north', extra_sinuous=True)
```
Now when you delete "turbo," you can be certain any user code that relies on it will fail loudly. If your library also supports Python 2, there's no shame in that; you can simulate the star syntax thus ([credit to Brett Slatkin][17]):
```
# Your library code, Python 2 compatible.
def move(direction, **kwargs):
mode = kwargs.pop('mode', 'slither')
turbo = kwargs.pop('turbo', False)
sinuous = kwargs.pop('extra_sinuous', False)
lyft = kwargs.pop('hail_lyft', False)
if kwargs:
raise TypeError('Unexpected kwargs: %r'
% kwargs)
# ...
```
Requiring keyword arguments is a wise choice, but it requires foresight. If you allow an argument to be passed positionally, you cannot convert it to keyword-only in a later release. So, add the star now. You can observe in the asyncio API that it uses the star pervasively in constructors, methods, and functions. Even though "Lock" only takes one optional parameter so far, the asyncio developers added the star right away. This is providential.
```
# In asyncio.
class Lock:
def __init__(self, *, loop=None):
# ...
```
Now we've gained the wisdom to change methods and parameters while keeping our covenant with users. The time has come to try the most challenging kind of evolution: changing behavior without changing either methods or parameters.
### Changing behavior
Let's say your creature is a rattlesnake, and you want to teach it a new behavior.
![Rattlesnake][18]
Sidewinding! The creature's body will appear the same, but its behavior will change. How can we prepare it for this step of its evolution?
![][19]
Image by HCA [[CC BY-SA 4.0][20]], [via Wikimedia Commons][21], modified by Opensource.com
A responsible creator can learn from the following example in the Python standard library, when behavior changed without a new function or parameters. Once upon a time, the os.stat function was introduced to get file statistics, like the creation time. At first, times were always integers.
```
>>> os.stat('file.txt').st_ctime
1540817862
```
One day, the core developers decided to use floats for os.stat times to give sub-second precision. But they worried that existing user code wasn't ready for the change. They created a setting in Python 2.3, "stat_float_times," that was false by default. A user could set it to True to opt into floating-point timestamps.
```
>>> # Python 2.3.
>>> os.stat_float_times(True)
>>> os.stat('file.txt').st_ctime
1540817862.598021
```
Starting in Python 2.5, float times became the default, so any new code written for 2.5 and later could ignore the setting and expect floats. Of course, you could set it to False to keep the old behavior or set it to True to ensure the new behavior in all Python versions, and prepare your code for the day when stat_float_times is deleted.
Ages passed. In Python 3.1, the setting was deprecated to prepare people for the distant future and finally, after its decades-long journey, [the setting was removed][22]. Float times are now the only option. It's a long road, but responsible deities are patient because we know this gradual process has a good chance of saving users from unexpected behavior changes.
#### Tenth covenant: Change behavior gradually
Here are the steps:
* Add a flag to opt into the new behavior, default False, warn if it's False
* Change default to True, deprecate flag entirely
* Remove the flag
If you follow semantic versioning, the versions might be like so:
Library version | Library API | User code
---|---|---
| |
1.0 | No flag | Expect old behavior
1.1 | Add flag, default False,
warn if it's False | Set flag True,
handle new behavior
2.0 | Change default to True,
deprecate flag entirely | Handle new behavior
3.0 | Remove flag | Handle new behavior
You need _two_ major releases to complete the maneuver. If you had gone straight from "Add flag, default False, warn if it's False" to "Remove flag" without the intervening release, your users' code would be unable to upgrade. User code written correctly for 1.1, which sets the flag to True and handles the new behavior, must be able to upgrade to the next release with no ill effect except new warnings, but if the flag were deleted in the next release, that code would break. A responsible deity never violates the Twisted policy: "The First One's Always Free."
### The responsible creator
![Demeter][23]
Our 10 covenants belong loosely in three categories:
**Evolve cautiously**
1. Avoid bad features
2. Minimize features
3. Keep features narrow
4. Mark experimental features "provisional"
5. Delete features gently
**Record history rigorously**
1. Maintain a changelog
2. Choose a version scheme
3. Write an upgrade guide
**Change slowly and loudly**
1. Add parameters compatibly
2. Change behavior gradually
If you keep these covenants with your creature, you'll be a responsible creator deity. Your creature's body can evolve over time, forever improving and adapting to changes in its environment but without sudden changes the creature isn't prepared for. If you maintain a library, keep these promises to your users and you can innovate your library without breaking the code of the people who rely on you.
* * *
_This article originally appeared on[A. Jesse Jiryu Davis's blog][24] and is republished with permission._
Illustration credits:
* [The World's Progress, The Delphian Society, 1913][25]
* [Essay Towards a Natural History of Serpents, Charles Owen, 1742][26]
* [On the batrachia and reptilia of Costa Rica: With notes on the herpetology and ichthyology of Nicaragua and Peru, Edward Drinker Cope, 1875][27]
* [Natural History, Richard Lydekker et. al., 1897][28]
* [Mes Prisons, Silvio Pellico, 1843][29]
* [Tierfotoagentur / m.blue-shadow][30]
* [Los Angeles Public Library, 1930][31]
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/5/api-evolution-right-way
作者:[A. Jesse][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/emptysquare
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/browser_desktop_website_checklist_metrics.png?itok=OKKbl1UR (Browser of things)
[2]: https://opensource.com/sites/default/files/uploads/praise-the-creator.jpg (Serpents)
[3]: https://opensource.com/sites/default/files/uploads/bite.jpg (Man being chased by an alligator)
[4]: https://bugs.python.org/issue13936
[5]: https://opensource.com/sites/default/files/uploads/feathers.jpg (Serpents with and without feathers)
[6]: https://bugs.python.org/issue32591
[7]: https://opensource.com/sites/default/files/uploads/horns.jpg (Serpent with horns)
[8]: https://opensource.com/sites/default/files/uploads/lizard-to-snake.jpg (Lizard transformed to snake)
[9]: https://opensource.com/sites/default/files/uploads/mammal.jpg (A mouse)
[10]: https://bugs.python.org/issue32253
[11]: https://opensource.com/sites/default/files/uploads/scale.jpg (Balance scales)
[12]: https://semver.org
[13]: https://www.python.org/dev/peps/pep-0440/
[14]: https://opensource.com/sites/default/files/uploads/skink.jpg (A skink)
[15]: https://twistedmatrix.com/documents/current/core/development/policy/compatibility-policy.html
[16]: https://opensource.com/sites/default/files/uploads/loudly.jpg (Woman riding an alligator)
[17]: http://www.informit.com/articles/article.aspx?p=2314818
[18]: https://opensource.com/sites/default/files/uploads/rattlesnake.jpg (Rattlesnake)
[19]: https://opensource.com/sites/default/files/articles/neonate_sidewinder_sidewinding_with_tracks_unlabeled.png
[20]: https://creativecommons.org/licenses/by-sa/4.0
[21]: https://commons.wikimedia.org/wiki/File:Neonate_sidewinder_sidewinding_with_tracks_unlabeled.jpg
[22]: https://bugs.python.org/issue31827
[23]: https://opensource.com/sites/default/files/uploads/demeter.jpg (Demeter)
[24]: https://emptysqua.re/blog/api-evolution-the-right-way/
[25]: https://www.gutenberg.org/files/42224/42224-h/42224-h.htm
[26]: https://publicdomainreview.org/product-att/artist/charles-owen/
[27]: https://archive.org/details/onbatrachiarepti00cope/page/n3
[28]: https://www.flickr.com/photos/internetarchivebookimages/20556001490
[29]: https://www.oldbookillustrations.com/illustrations/stationery/
[30]: https://www.alamy.com/mediacomp/ImageDetails.aspx?ref=D7Y61W
[31]: https://www.vintag.es/2013/06/riding-alligator-c-1930s.html

View File

@ -1,130 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (How to SSH into a Raspberry Pi [Beginners Tip])
[#]: via: (https://itsfoss.com/ssh-into-raspberry/)
[#]: author: (Chinmay https://itsfoss.com/author/chinmay/)
How to SSH into a Raspberry Pi [Beginners Tip]
======
_**In this Raspberry Pi article series, youll learn how to enable SSH in Raspberry Pi and then how to SSH into a Raspberry Pi device.**_
Out of all the things you can do with [Raspberry Pi][1], using it as a server in a home network is very popular. The tiny footprint and low power consumption makes it a perfect device to run light weight servers.
One of the things you should be able to do in such a case is run commands on your Raspberry Pi without needing to plug in a display, keyboard, mouse and having to move yourself to the location of your Raspberry Pi each time.
You achieve this by logging into your Raspberry Pi via SSH ([Secure Shell][2]) from any other computer, your laptop, desktop or even your phone. Let me show you how
### How to SSH into Raspberry Pi
![][3]
I assume that you are [running Raspbian on your Pi][4] and have successfully connected to a network via Ethernet or WiFi. Its important that your Raspberry Pi is connected to a network otherwise you wont be able to connect to it via SSH (sorry for stating the obvious).
#### Step 1: Enable SSH on Raspberry Pi
SSH is disabled by default in Raspberry Pi, hence youll have to enable it when you turn on the Pi after a fresh installation of Raspbian.
First go to the Raspberry Pi configuration window by navigating through the menu.
![Raspberry Pi Menu, Raspberry Pi Configuration][5]
Now, go to the interfaces tab, enable SSH and restart your Pi.
![Enable SSH on Raspberry Pi][6]
You can also enable SSH without via the terminal. Just enter the command _**sudo raspi-config**_ and then go to Advanced Options to enable SSH.
#### Step 2. Find the IP Address of Raspberry Pi
In most cases your Raspberry Pi will be assigned a local IP address which looks like **192.168.x.x** or **10.x.x.x**. You can [use various Linux commands to find the IP address][7].
[][8]
Suggested read This Linux Malware Targets Unsecure Raspberry Pi Devices
I am using the good old ifconfig command here but you can also use _**ip address**_.
```
ifconfig
```
![Raspberry Pi Network Configuration][9]
This command shows all the list of active network adapters and their configuration. The first entry( **eth0** ) shows IP address as **192.168.2.105** which is valid.I have used Ethernet to connect my Raspberry Pi to the network, hence it is under **eth0**. If you use WiFi check under the entry named **wlan0** .
You can also find out the IP address by other means like checking the network devices list on your router/modem.
#### Step 3. SSH into your Raspberry Pi
Now that you have enabled SSH and found out your IP address you can go ahead and SSH into your Raspberry Pi from any other computer. Youll also need the username and the password for the Raspberry Pi.
Default Username and Password is:
* username: pi
* password: raspberry
If you have changed the default password then use the new password instead of the above. Ideally you must change the default password. In the past, a [malware infected thousands of Raspberry Pi devices that were using the default username and password][8].
Open a terminal (on Mac and Linux) on the computer from which you want to SSH into your Pi and type the command below. On Windows, you can use a SSH client like [Putty][10].
Here, use the IP address you found out in the previous step.
```
ssh [email protected]
```
_**Note: Make sure your Raspberry Pi and the computer you are using to SSH into your Raspberry Pi are connected to the same network**_.
![SSH through terminal][11]
Youll see a warning the first time, type **yes** and press enter.
![Type the password \(default is raspberry\)][12]
Now, type in the password and press enter.
![Successful Login via SSH][13]
On a successful login youll be presented with the terminal of your Raspberry Pi. Now you can any commands on your Raspberry Pi through this terminal remotely(within the current network) without having to access your Raspberry Pi physically.
[][14]
Suggested read Speed Up Ubuntu Unity On Low End Systems [Quick Tip]
Furthermore you can also set up SSH-Keys so that you dont have to type in the password every time you log in via SSH, but thats a different topic altogether.
I hope you were able to SSH into your Raspberry Pi after following this tutorial. Let me know how you plan to use your Raspberry Pi in the comments below!
--------------------------------------------------------------------------------
via: https://itsfoss.com/ssh-into-raspberry/
作者:[Chinmay][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://itsfoss.com/author/chinmay/
[b]: https://github.com/lujun9972
[1]: https://www.raspberrypi.org/
[2]: https://en.wikipedia.org/wiki/Secure_Shell
[3]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/ssh-into-raspberry-pi.png?resize=800%2C450&ssl=1
[4]: https://itsfoss.com/tutorial-how-to-install-raspberry-pi-os-raspbian-wheezy/
[5]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/Raspberry-pi-configuration.png?ssl=1
[6]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/enable-ssh-raspberry-pi.png?ssl=1
[7]: https://linuxhandbook.com/find-ip-address/
[8]: https://itsfoss.com/raspberry-pi-malware-threat/
[9]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/ifconfig-rapberry-pi.png?ssl=1
[10]: https://itsfoss.com/putty-linux/
[11]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/SSH-into-pi-warning.png?fit=800%2C199&ssl=1
[12]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/SSH-into-pi-password.png?fit=800%2C202&ssl=1
[13]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/SSH-into-Pi-successful-login.png?fit=800%2C306&ssl=1
[14]: https://itsfoss.com/speed-up-ubuntu-unity-on-low-end-system/

View File

@ -1,123 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (geekpi)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (PiShrink Make Raspberry Pi Images Smaller)
[#]: via: (https://www.ostechnix.com/pishrink-make-raspberry-pi-images-smaller/)
[#]: author: (sk https://www.ostechnix.com/author/sk/)
PiShrink Make Raspberry Pi Images Smaller
======
![Make Raspberry Pi Images Smaller With PiShrink In Linux][1]
**Raspberry Pi** requires no introduction. It is a small, affordable and credit-card sized computer that can be connected to a Monitor or TV. We can attach a standard keyboard and mouse and use it as a full-blown desktop computer to do everyday tasks, such Internet browsing, playing videos/games, word processing and spreadsheet making and a lot more. It has been mainly developed for teaching Computer science in schools. Nowadays, Raspberry Pi is widely being used in colleges, small-medium organizations and institutes to teach coding. If you own a Raspberry Pi device, you might want to check out a bash script named **“PiShrink”** , which is used to make Raspberry Pi Images smaller. PiShrink will automatically shrink a pi image that will then resize to the max size of the SD card on boot. This will make putting the image back onto the SD card faster and the shrunk images will compress better. This can be useful to fit the large size images in your SD card. In this brief guide, we are going to learn to shrink Raspberry images to smaller size in Unix-like systems.
### Installing PiShrink
To install PiShrink on your Linux box, first download the latest version using command:
```
$ wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
```
Next, make the downloaded PiShrink binary as executable:
```
$ chmod +x pishrink.sh
```
Finally, move it your path:
```
$ sudo mv pishrink.sh /usr/local/bin/
```
### Make Raspberry Pi Images Smaller
As you may already know, **Raspbian** is the official operating system for all models of Raspberry Pi. The Raspberry foundation has developed **Raspberry Pi Desktop** version for PC and Mac. You can create a live cd, run it in virtual machine and even install it in your desktop as well. There are also few unofficial OS images available for Raspberry Pi. For the purpose of testing, Ive downloaded the official Raspbian OS from the [**official download page**][2].
Unzip the downloaded OS image:
```
$ unzip 2019-04-08-raspbian-stretch-lite.zip
```
The above command will extract contents of **2019-04-08-raspbian-stretch-lite.zip** file in the current working directory.
Let check the actual size of the extracted file:
```
$ du -h 2019-04-08-raspbian-stretch-lite.img
1.7G 2019-04-08-raspbian-stretch-lite.img
```
As you can see, the size of the extracted Raspberry OS img file is **1.7G**.
Now, shrink this files size using PiShrink like below:
```
$ sudo pishrink.sh 2019-04-08-raspbian-stretch-lite.img
```
Sample output:
```
Creating new /etc/rc.local
rootfs: 39795/107072 files (0.1% non-contiguous), 239386/428032 blocks
resize2fs 1.45.0 (6-Mar-2019)
resize2fs 1.45.0 (6-Mar-2019)
Resizing the filesystem on /dev/loop1 to 280763 (4k) blocks.
Begin pass 3 (max = 14)
Scanning inode table XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Begin pass 4 (max = 3728)
Updating inode references XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
The filesystem on /dev/loop1 is now 280763 (4k) blocks long.
Shrunk 2019-04-08-raspbian-stretch-lite.img from 1.7G to 1.2G
```
[![Make Raspberry Pi Images Smaller Using PiShrink][1]][3]
Make Raspberry Pi Images Smaller Using PiShrink
As you see in the above output, the size of the Rasberry Pi image has been reduced to **1.2G**.
You can also use **-s** flag to skip the autoexpanding part of the process.
```
$ sudo pishrink.sh -s 2019-04-08-raspbian-stretch-lite.img newpi.img
```
This will create a copy of source img file (i.e 2019-04-08-raspbian-stretch-lite.img) into a new img file (newpi.img) and work on it. For more details, check the official GitHub page given at the end.
And, thats all for now. Hope this was useful. More good stuffs to come. Stay tuned!
And, thats all for now.
**Resource:**
* [**PiShrink GitHub Repository**][4]
* [**Raspberry Pi website**][5]
--------------------------------------------------------------------------------
via: https://www.ostechnix.com/pishrink-make-raspberry-pi-images-smaller/
作者:[sk][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://www.ostechnix.com/author/sk/
[b]: https://github.com/lujun9972
[1]: https://www.ostechnix.com/wp-content/uploads/2019/05/pishrink-720x340.png
[2]: https://www.raspberrypi.org/downloads/
[3]: http://www.ostechnix.com/wp-content/uploads/2019/05/pishrink-1.png
[4]: https://github.com/Drewsif/PiShrink
[5]: https://www.raspberrypi.org/

View File

@ -0,0 +1,719 @@
[#]: collector: (lujun9972)
[#]: translator: (MjSeven)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (API evolution the right way)
[#]: via: (https://opensource.com/article/19/5/api-evolution-right-way)
[#]: author: (A. Jesse https://opensource.com/users/emptysquare)
API 演进的正确方式
======
负责任的库作者与其用户保持的十个约定。
![Browser of things][1]
想象你是一个创造之神,为一个生物设计一个身体。出于仁慈,你希望这个生物能随着时间进化:首先,因为它必须对环境的变化作出反应,其次,因为你的智慧在增长,你想到了更好的设计。它不应该永远留在同一个身体里!
![Serpents][2]
然而,该生物可能依赖于其目前解剖学的特征。你不能在没有警告的情况下添加翅膀或改变它的比例。它需要一个有序的过程来适应新的身体。作为一个负责任的设计师,你如何才能温柔地引导这种生物走向更大的进步呢?
对于负责任的库维护者也是如此。我们向依赖我们代码的人保证我们的承诺:我们发布 bug 修复和有用的新特性。如果对库的未来有利,我们有时会删除特性。我们不断创新,但我们不会破坏使用我们库的人的代码。我们怎样才能一次实现所有这些目标呢?
### 添加有用的特性
你的库不应该永远保持不变:你应该添加一些特性,使你的库更适合用户。例如,如果你有一个爬行动物类,并且有翅膀飞行是有用的,那就去添加吧。
```
class Reptile:
@property
def teeth(self):
return 'sharp fangs'
# 如果 wings 是有用的,那就添加它!
@property
def wings(self):
return 'majestic wings'
```
但要注意,特性是有风险的。考虑 Python 标准库中以下功能,看看它出了什么问题。
```
bool(datetime.time(9, 30)) == True
bool(datetime.time(0, 0)) == False
```
这很奇怪:将任何时间对象转换为布尔值都会得到 True但午夜时间除外。更糟糕的是时区感知时间的规则更加奇怪。
我已经写了十多年的 Python 了,但直到上周才发现这条规则。这种奇怪的行为会在用户代码中引起什么样的 bug
考虑一个日历应用程序,它带有一个创建事件的函数。如果一个事件有一个结束时间,那么函数也应该要求它有一个开始时间。
```
def create_event(day,
start_time=None,
end_time=None):
if end_time and not start_time:
raise ValueError("Can't pass end_time without start_time")
# 女巫集会从午夜一直开到凌晨 4 点
create_event(datetime.date.today(),
datetime.time(0, 0),
datetime.time(4, 0))
```
不幸的是,对于女巫来说,从午夜开始的事件无法通过验证。当然,一个了解午夜怪癖的细心程序员可以正确地编写这个函数。
```
def create_event(day,
start_time=None,
end_time=None):
if end_time is not None and start_time is None:
raise ValueError("Can't pass end_time without start_time")
```
但这种微妙之处令人担忧。如果一个库作者想要创建一个对用户有害的 API那么像午夜的布尔转换这样的“特性”很有效。
![Man being chased by an alligator][3]
但是,负责任的创建者的目标是使你的库易于正确使用。
这个功能是由 Tim Peters 在 2002 年首次编写 datetime 模块时造成的。即时是像 Tim 这样的 Python 创始人也会犯错误。[这个怪异后来被消除了][4],现在所有时间的布尔值都是 True。
```
# Python 3.5 以后
bool(datetime.time(9, 30)) == True
bool(datetime.time(0, 0)) == True
```
不知道午夜古怪之处的程序员现在可以从晦涩的 bug 中解脱出来,但是一想到任何依赖于古怪的旧行为的代码现在没有注意变化,我会感到紧张。如果根本不实现这个糟糕的特性,情况会更好。这就引出了库维护者的第一个承诺:
#### 第一个约定:避免糟糕的特性
最痛苦的变化是你必须删除一个特性。一般来说,避免糟糕特性的一种方法是添加少的特性!没有充分的理由,不要使用公共方法、类、功能或属性。因此:
#### 第二个约定:最小化特性
特性就像孩子在充满激情的瞬间孕育to 校正我怀疑作者在开车可是我没有证据它们必须得到多年的支持。不要因为你能做傻事就去做傻事。不要画蛇添足to 校正:我认为这里内在是这个意思)!
![Serpents with and without feathers][5]
但是,当然,在很多情况下,用户需要你的库中尚未提供的东西,你如何选择合适的功能给他们?以下另一个警示故事。
### 一个来自 asyncio 的警示故事
你可能知道,当你调用一个协程函数,它会返回一个协程对象:
```
async def my_coroutine():
pass
print(my_coroutine())
```
```
<coroutine object my_coroutine at 0x10bfcbac8>
```
你的代码必须 "await" 这个对象以此来运行协程。很容易忘记这一点,所以 asyncio 的开发人员想要一个“调试模式”来捕捉这个错误。但协程在没有 await 的情况下被销毁时,调试模式将打印一个警告,并在其创建的行上进行回溯。
当 Yury Selivanov 实现调试模式时他在其基础上添加了一个“协程装饰器”特性。装饰器是一个函数它接收一个协程并返回所有内容。Yury 使用它在每个协程上安装警告逻辑,但是其他人可以使用它将协程转换为字符串 "hi!"。
```
import sys
def my_wrapper(coro):
return 'hi!'
sys.set_coroutine_wrapper(my_wrapper)
async def my_coroutine():
pass
print(my_coroutine())
```
```
hi!
```
这是一个地狱般的定制。它改变了 "async" 的含义。一次调用 `set_coroutine_wrapper` 将在全局永久改变所有的协程函数。正如 [Nathaniel Smith 所说][6]:“一个有问题的 API” 很容易被误用,必须被删除。如果异步开发人员能够更好地按照其目标来设计该特性,他们就可以避免删除该特性的痛苦。负责任的创建者必须牢记这一点:
#### 第三个约定:保持特性单一
幸运的是Yury 有良好的判断力,他将特性标记为临时,所以 asyncio 用户知道不能依赖它。Nathaniel 可以用更单一的功能替换 **set_coroutine_wrapper** ,该特性只定制回溯深度。
```
import sys
sys.set_coroutine_origin_tracking_depth(2)
async def my_coroutine():
pass
print(my_coroutine())
```
```
<coroutine object my_coroutine at 0x10bfcbac8>
RuntimeWarning:'my_coroutine' was never awaited
Coroutine created at (most recent call last)
File "script.py", line 8, in <module>
print(my_coroutine())
```
这样好多了。没有其他全局设置可以更改协程的类型,因此 asyncio 用户无需编写防御代码。神灵应该像 Yury 一样有远见。
#### 第四个约定:标记实验特征“临时”
如果你只是预感你的生物需要犄角和四叉舌,那就介绍一下这些特性,但将它们标记为“临时”。
![Serpent with horns][7]
你可能会发现犄角是无关紧要的,但是四叉舌是有用的。在库的下一个版本中,你可以删除前者并标记后者。
### 删除特性
无论我们如何明智地指导我们的生物进化,总会有一天最好删除一个官方特征。例如,你可能已经创建了一只蜥蜴,现在你选择删除它的腿。也许你想把这个笨拙的家伙变成一条时尚而现代的蟒蛇。
![Lizard transformed to snake][8]
删除特性主要有两个原因。首先,通过用户反馈或者你自己不断增长的智慧,你可能会发现某个特性是个坏主意。午夜的古怪行为就是这种情况。或者,最初该特性可能已经很好地适应了你的库环境,但现在生态环境发生了变化,也许另一个神发明了哺乳动物,你的生物想要挤进哺乳动物的小洞穴里,吃掉里面美味的哺乳动物,所以它不得不失去双腿。
![A mouse][9]
同样Python 标准库会根据语言本身的变化删除特性。考虑 asyncio 的 Lock 功能,在把 "await" 作为一个关键字添加进来之前,它一直在等待:
```
lock = asyncio.Lock()
async def critical_section():
await lock
try:
print('holding lock')
finally:
lock.release()
```
但是现在,我们可以做“锁同步”:
```
lock = asyncio.Lock()
async def critical_section():
async with lock:
print('holding lock')
```
新方法好多了!很短,并且在一个大函数中使用其他 try-except 块时不容易出错。因为“尽量找一种,最好是唯一一种明显的解决方案”,[旧语法在 Python 3.7 中被弃用][10],并且很快就会被禁止。
不可避免的是,生态变化会对你的代码产生影响,因此要学会温柔地删除特性。在此之前,请考虑删除它的成本或好处。负责任的维护者不愿意让用户更改大量代码或逻辑。(还记得 Python 3 在重新添加 "u" 字符串前缀之前删除它是多么痛苦吗?)如果代码删除是机械性的,就像一个简单的搜索和替换,或者如果该特性是危险的,那么它可能值得删除。
#### 是否删除特性
![Balance scales][11]
Con | Pro
---|---
代码必须改变 | 改变是机械性的
逻辑必须改变 | 特性是危险的
就我们饥饿的蜥蜴而言,我们决定删除它的腿,这样它就可以滑进老鼠洞里吃掉它。我们该怎么做呢?我们可以删除 **walk** 方法,像下面一样修改代码:
```
class Reptile:
def walk(self):
print('step step step')
```
变成这样:
```
class Reptile:
def slither(self):
print('slide slide slide')
```
这不是一个好主意,这个生物习惯于走路!或者,就库而言,你的用户拥有依赖于现有方法的代码。当他们升级到最新库版本时,他们的代码将会崩溃。
```
# 用户的代码,哦,不!
Reptile.walk()
```
因此,负责任的创建者承诺:
#### 第五条预定:温柔地删除
温柔删除一个特性需要几个步骤。从用腿走路的蜥蜴开始,首先添加新方法 "slither"。接下来,弃用旧方法。
```
import warnings
class Reptile:
def walk(self):
warnings.warn(
"walk is deprecated, use slither",
DeprecationWarning, stacklevel=2)
print('step step step')
def slither(self):
print('slide slide slide')
```
Python 的 warnings 模块非常强大。默认情况下,它会将警告输出到 stderr每个代码位置只显示一次但你可以在其它选项中禁用警告或将其转换为异常。
一旦将这个警告添加到库中PyCharm 和其他 IDE 就会使用删除线呈现这个被弃用的方法。用户马上就知道该删除这个方法。
`Reptile().walk()`
<to 校正这里 walk 上有一个横线表示删除>
当他们使用升级后的库运行代码时会发生什么?
```
$ python3 script.py
DeprecationWarning: walk is deprecated, use slither
script.py:14: Reptile().walk()
step step step
```
默认情况下,他们会在 stderr 上看到警告,但脚本会成功并打印 "step step step"。警告的回溯显示必须修复用户代码的哪一行。(这就是 "stacklevel" 参数的作用:它显示了用户需要更改的调用,而不是库中生成警告的行。)请注意,错误消息有指导意义,它描述了库用户迁移到新版本必须做的事情。
你的用户将希望测试他们的代码并证明他们没有调用不推荐的库方法。仅警告不会使单元测试失败但异常会失败。Python 有一个命令行选项,可以将弃用警告转换为异常。
```
> python3 -Werror::DeprecationWarning script.py
Traceback (most recent call last):
File "script.py", line 14, in <module>
Reptile().walk()
File "script.py", line 8, in walk
DeprecationWarning, stacklevel=2)
DeprecationWarning: walk is deprecated, use slither
```
现在,"step step step" 没有输出出来,因为脚本以一个错误终止。
因此,一旦你发布了库的一个版本,该版本会警告已启用的 "walk" 方法,你就可以在下一个版本中安全地删除它。对吧?
考虑一下你的库用户在他们项目的 requirements 中可能有什么。
```
# 用户的 requirements.txt 显示 reptile 包的依赖关系
reptile
```
下次他们部署代码时,他们将安装最新版本的库。如果他们尚未处理所有的弃用,那么他们的代码将会崩溃,因为代码仍然依赖 "walk"。你需要温柔一点,你必须向用户做出三个承诺:维护更改日志,选择版本方案和编写升级指南。
#### 第六个约定:维护变更日志
你的库必须有更改日志,其主要目的是宣布用户所依赖的功能何时被弃用或删除。
---
#### 版本 1.1 中的更改
**新特性**
* 新功能 Reptile.slither()
**弃用**
* Reptile.walk() 已弃用,将在 2.0 版本中删除,请使用 slither()
---
负责任的创建者使用版本号来表示库发生了怎样的变化,以便用户能够对升级做出明智的决定。“版本方案”是一种用于交流变化速度的语言。
#### 第七个约定:选择一个版本方案
有两种广泛使用的方案,[语义版本控制][12]和基于时间的版本控制。我推荐任何库都进行语义版本控制。Python 的风格在 [PEP 440][13] 中定义,像 **pip** 这样的工具可以理解语义版本号。
如果你为库选择语义版本控制,你可以使用版本号温柔地删除腿,例如:
> 1.0: First "stable" release, with walk()
> 1.1: Add slither(), deprecate walk()
> 2.0: Delete walk()
你的用户依赖于你的库的版本应该有一个范围,例如:
```
# 用户的 requirements.txt
reptile>=1,<2
```
这允许他们在主要版本中自动升级,接收错误修正并可能引发一些弃用警告,但不会升级到 _下_ 个主要版本并冒着破坏其代码的更改的风险。
如果你遵循基于时间的版本控制,则你的版本可能会编号:
> 2017.06.0: A release in June 2017
> 2018.11.0: Add slither(), deprecate walk()
> 2019.04.0: Delete walk()
用户可以依赖于你的库:
```
# User's requirements.txt for time-based version.
reptile==2018.11.*
```
这允许他们在一个主要版本中自动升级,接收错误修复,并可能引发一些弃用警告,但不能升级到 _下_ 个主要版本,并冒着改变破坏代码的风险。
如果你遵循基于时间的版本控制,你的版本号可能是这样:
> 2017.06.0: A release in June 2017
> 2018.11.0: Add slither(), deprecate walk()
> 2019.04.0: Delete walk()
用户可以依赖你的库:
```
# 用户的 requirements.txt对于基于时间的版本
reptile==2018.11.*
```
这非常棒,但你的用户如何知道你的版本方案,以及如何测试代码来进行弃用呢?你必须告诉他们如何升级。
#### 第八个约定:写一个升级指南
下面是一个负责任的库创建者如何指导用户:
---
#### 升级到 2.0
**从弃用的 API 迁移**
请参阅更改日志以了解已弃用的特性。
**启用弃用警告**
升级到 1.1 并使用以下代码测试代码:
`python -Werror::DeprecationWarning`
​​​​​​现在可以安全地升级了。
---
你必须通过向用户显示命令行选项来教会用户如何处理弃用警告。并非所有 Python 程序员都知道这一点 - 当然,我每次都必须查找语法。注意,你必须 _release_ 一个版本,它输出来自每个弃用的 API 的警告以便用户可以在再次升级之前使用该版本进行测试。在本例中1.1 版本是小版本。它允许你的用户逐步重写代码,分别修复每个弃用警告,直到他们完全迁移到最新的 API。他们可以彼此独立地测试代码和库的更改并隔离 bug 的原因。
如果你选择语义版本控制,则此过渡期将持续到下一个主要版本,从 1.x 到 2.0,或从 2.x 到 3.0 以此类推。删除生物腿部的温柔方法是至少给它一个版本来调整其生活方式。不要一次性把腿删掉!
![A skink][14]
版本号,弃用警告,更改日志和升级指南可以协同工作,在不违背与用户约定的情况下温柔地改进你的库。[Twisted 项目的兼容性政策][15] 解释的很漂亮:
> "The First One's Always Free"
>
> Any application which runs without warnings may be upgraded one minor version of Twisted.
>
> In other words, any application which runs its tests without triggering any warnings from Twisted should be able to have its Twisted version upgraded at least once with no ill effects except the possible production of new warnings.
现在,我们的造物之神已经获得了智慧和力量,可以通过添加方法来添加特性,并温柔地删除它们。我们还可以通过添加参数来添加特性,但这带来了新的难度。你准备好了吗?
### 添加参数
想象一下,你只是给了你的蛇形生物一对翅膀。现在你必须允许它选择是滑行还是飞行。目前它的 "move" 功能只接受一个参数。
```
# 你的库代码
def move(direction):
print(f'slither {direction}')
# 用户的应用
move('north')
```
你想要添加一个 "mode" 参数,但如果用户升级库,这会破坏他们的代码,因为他们只传递一个参数。
```
# 你的库代码
def move(direction, mode):
assert mode in ('slither', 'fly')
print(f'{mode} {direction}')
# 一个用户的代码,出现错误!
move('north')
```
一个真正聪明的创建者者承诺不会以这种方式破坏用户的代码。
#### 第九条约定:兼容地添加参数
要保持这个约定,请使用保留原始行为的默认值添加每个新参数。
```
# 你的库代码
def move(direction, mode='slither'):
assert mode in ('slither', 'fly')
print(f'{mode} {direction}')
# 用户的应用
move('north')
```
随着时间推移,参数是函数演化的自然历史。它们首先列出最老的,每个都有默认值。库用户可以传递关键字参数以选择特定的新行为,并接受所有其他行为的默认值。
```
# 你的库代码
def move(direction,
mode='slither',
turbo=False,
extra_sinuous=False,
hail_lyft=False):
# ...
# 用户应用
move('north', extra_sinuous=True)
```
但是有一个危险,用户可能会编写如下代码:
```
# 用户应用,简写
move('north', 'slither', False, True)
```
如果在你在库的下一个主要版本中去掉其中一个参数,例如 "turbo",会发生什么?
```
# 你的库代码,下一个主要版本中 "turbo" 被删除
def move(direction,
mode='slither',
extra_sinuous=False,
hail_lyft=False):
# ...
# 用户应用,简写
move('north', 'slither', False, True)
```
用户的代码仍然编译,这是一件坏事。代码停止了曲折的移动并开始欢呼 Lyft这不是它的本意。我相信你可以预测我接下来要说的内容删除参数需要几个步骤。当然首先弃用 "trubo" 参数。我喜欢这种技术,它可以检测任何用户的代码是否依赖于这个参数。
```
# 你的库代码
_turbo_default = object()
def move(direction,
mode='slither',
turbo=_turbo_default,
extra_sinuous=False,
hail_lyft=False):
if turbo is not _turbo_default:
warnings.warn(
"'turbo' is deprecated",
DeprecationWarning,
stacklevel=2)
else:
# The old default.
turbo = False
```
但是你的用户可能不会注意到警告。警告声音不是很大:它们可以在日志文件中被抑制或丢失。用户可能会漫不经心地升级到库的下一个主要版本,即删除 "turbo" 的版本。他们的代码运行将没有错误,默默做错误的事情!正如 Python 之禅所说:“错误绝不应该被默默 pass”。实际上爬行动物的听力很差所有当它们犯错误时你必须非常大声地纠正它们。
![Woman riding an alligator][16]
保护用户的最佳方法是使用 Python 3 的星型语法,它要求调用者传递关键字参数。
```
# 你的库代码
# All arguments after "*" must be passed by keyword.
def move(direction,
*,
mode='slither',
turbo=False,
extra_sinuous=False,
hail_lyft=False):
# ...
# 用户代码,简写
# 错误!不能使用位置参数,关键字参数是必须的
move('north', 'slither', False, True)
```
有了这个星,以下唯一允许的语法:
```
# 用户代码
move('north', extra_sinuous=True)
```
现在,当你删除 "turbo" 时,你可以确定任何依赖于它的用户代码都会明显地提示失败。如果你的库也支持 Python2这没有什么大不了。你可以模拟星型语法[归功于 Brett Slatkin][17]
```
# 你的库代码,兼容 Python 2
def move(direction, **kwargs):
mode = kwargs.pop('mode', 'slither')
turbo = kwargs.pop('turbo', False)
sinuous = kwargs.pop('extra_sinuous', False)
lyft = kwargs.pop('hail_lyft', False)
if kwargs:
raise TypeError('Unexpected kwargs: %r'
% kwargs)
# ...
```
要求关键字参数是一个明智的选择,但它需要远见。如果允许按位置传递参数,则不能仅在以后的版本中将其转换为仅关键字。所以,现在加上星号,你可以在 asyncio API 中观察到,它在构造函数、方法和函数中普遍使用星号。尽管到目前为止,"Lock" 只接受一个可选参数,但 asyncio 开发人员立即添加了星号。这是幸运的。
```
# In asyncio.
class Lock:
def __init__(self, *, loop=None):
# ...
```
现在,我们已经获得了改变方法和参数的智慧,同时保持与用户的约定。现在是时候尝试最具挑战性的进化了:在不改变方法或参数的情况下改变行为。
### 改变行为
假设你创造的生物是一条响尾蛇,你想教它一种新行为。
![Rattlesnake][18]
横向的!这个生物的身体看起来是一样的,但它的行为会发生变化。我们如何为这一进化步骤做好准备?
![][19]
Image by HCA [[CC BY-SA 4.0][20]], [via Wikimedia Commons][21], 由 Opensource.com 修改
当行为在没有新函数或新参数的情况下发生更改时,负责的创建者可以从 Python 标准库中学习。很久以前os 模块引入了stat 函数来获取文件统计信息,比如创建时间。起初,这个时间总是整数。
```
>>> os.stat('file.txt').st_ctime
1540817862
```
有一天,核心开发人员决定在 os.stat 中使用浮点数来提供亚秒级精度。但他们担心现有的用户代码还没有做好准备更改。于是他们在 Python 2.3 中创建了一个设置 "stat_float_times",默认情况下是 false 。用户可以将其设置为 True 来选择浮点时间戳。
```
>>> # Python 2.3.
>>> os.stat_float_times(True)
>>> os.stat('file.txt').st_ctime
1540817862.598021
```
从 Python 2.5 开始,浮点时间成为默认值,因此 2.5 及之后版本编写的任何新代码都可以忽略该设置并期望得到浮点数。当然,你可以将其设置为 False 以保持旧行为,或将其设置为 True 以确保所有 Python 版本都得到浮点数,并为删除 stat_float_times 的那一天准备代码。
多年过去了,在 Python 3.1 中,该设置已被弃用,以便为人们为遥远的未来做好准备,最后,经过数十年的旅程,[这个设置被删除][22]。浮点时间现在是唯一的选择。这是一个漫长的过程,但负责任的神灵是有耐心的,因为我们知道这个渐进的过程很有可能于意外的行为变化拯救用户。
#### 第十个约定:逐渐改变行为
以下是步骤:
* 添加一个标志来选择新行为,默认为 False如果为 False 则发出警告
* 将默认值更改为 True表示完全弃用标记
* 删除标志
如果你遵循语义版本控制,版本可能如下:
Library version | Library API | User code
---|---|---
| |
1.0 | 没有标志 | 期望的旧行为
1.1 | 添加标志,默认为 False如果是 False则警告 | 设置标志为 True处理新行为
2.0 | 改变默认为 True完全弃用标志 | 处理新行为
3.0 | 移除标志 | 处理新行为
你需要 _两_ 个主要版本来完成该操作。如果你直接从“添加标志,默认为 False如果是 False 则发出警告到“删除标志”,而没有中间版本,那么用户的代码将无法升级。为 1.1 正确编写的用户代码必须能够升级到下一个版本,除了新警告之外,没有任何不良影响,但如果在下一个版本中删除了该标志,那么该代码将崩溃。一个负责任的神明从不违反扭曲的政策:“第一个总是自由的”。
### 负责任的创建者
![Demeter][23]
我们的 10 个约定大致可以分为三类:
**谨慎发展**
1. 避免不良功能
2. 最小化特性
3. 保持功能单一
4. 标记实验特征“临时”
5. 温柔删除功能
**严格记录历史**
1. 维护更改日志
2. 选择版本方案
3. 编写升级指南
**缓慢而明显地改变**
1. 兼容添加参数
2. 逐渐改变行为
如果你对你所创造的物种保持这些约定,你将成为一个负责任的创造之神。你的生物的身体可以随着时间的推移而进化,永远改善和适应环境的变化,而不是在生物没有准备好就突然改变。如果你维护一个库,请向用户保留这些承诺,这样你就可以在不破坏依赖库的人的代码的情况下对库进行更新。
* * *
_这篇文章最初是在 [A. Jesse Jiryu Davis 的博客上'][24]出现的经允许转载。_
插图参考:
* [《世界进步》, Delphian Society, 1913][25]
* [《走进蛇的历史》, Charles Owen, 1742][26]
* [关于哥斯达黎加的 batrachia 和爬行动物,关于尼加拉瓜和秘鲁的爬行动物和鱼类学的记录, Edward Drinker Cope, 1875][27]
* [《自然史》, Richard Lydekker et. al., 1897][28]
* [Mes Prisons, Silvio Pellico, 1843][29]
* [Tierfotoagentur / m.blue-shadow][30]
* [洛杉矶公共图书馆, 1930][31]
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/5/api-evolution-right-way
作者:[A. Jesse][a]
选题:[lujun9972][b]
译者:[MjSeven](https://github.com/MjSeven)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/emptysquare
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/browser_desktop_website_checklist_metrics.png?itok=OKKbl1UR (Browser of things)
[2]: https://opensource.com/sites/default/files/uploads/praise-the-creator.jpg (Serpents)
[3]: https://opensource.com/sites/default/files/uploads/bite.jpg (Man being chased by an alligator)
[4]: https://bugs.python.org/issue13936
[5]: https://opensource.com/sites/default/files/uploads/feathers.jpg (Serpents with and without feathers)
[6]: https://bugs.python.org/issue32591
[7]: https://opensource.com/sites/default/files/uploads/horns.jpg (Serpent with horns)
[8]: https://opensource.com/sites/default/files/uploads/lizard-to-snake.jpg (Lizard transformed to snake)
[9]: https://opensource.com/sites/default/files/uploads/mammal.jpg (A mouse)
[10]: https://bugs.python.org/issue32253
[11]: https://opensource.com/sites/default/files/uploads/scale.jpg (Balance scales)
[12]: https://semver.org
[13]: https://www.python.org/dev/peps/pep-0440/
[14]: https://opensource.com/sites/default/files/uploads/skink.jpg (A skink)
[15]: https://twistedmatrix.com/documents/current/core/development/policy/compatibility-policy.html
[16]: https://opensource.com/sites/default/files/uploads/loudly.jpg (Woman riding an alligator)
[17]: http://www.informit.com/articles/article.aspx?p=2314818
[18]: https://opensource.com/sites/default/files/uploads/rattlesnake.jpg (Rattlesnake)
[19]: https://opensource.com/sites/default/files/articles/neonate_sidewinder_sidewinding_with_tracks_unlabeled.png
[20]: https://creativecommons.org/licenses/by-sa/4.0
[21]: https://commons.wikimedia.org/wiki/File:Neonate_sidewinder_sidewinding_with_tracks_unlabeled.jpg
[22]: https://bugs.python.org/issue31827
[23]: https://opensource.com/sites/default/files/uploads/demeter.jpg (Demeter)
[24]: https://emptysqua.re/blog/api-evolution-the-right-way/
[25]: https://www.gutenberg.org/files/42224/42224-h/42224-h.htm
[26]: https://publicdomainreview.org/product-att/artist/charles-owen/
[27]: https://archive.org/details/onbatrachiarepti00cope/page/n3
[28]: https://www.flickr.com/photos/internetarchivebookimages/20556001490
[29]: https://www.oldbookillustrations.com/illustrations/stationery/
[30]: https://www.alamy.com/mediacomp/ImageDetails.aspx?ref=D7Y61W
[31]: https://www.vintag.es/2013/06/riding-alligator-c-1930s.html

View File

@ -0,0 +1,130 @@
[#]: collector: (lujun9972)
[#]: translator: (tomjlw)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (How to SSH into a Raspberry Pi [Beginners Tip])
[#]: via: (https://itsfoss.com/ssh-into-raspberry/)
[#]: author: (Chinmay https://itsfoss.com/author/chinmay/)
如何 SSH 进入树莓派 [新手教程]
======
_**在这篇树莓派文章中,你将学到如何在树莓派中启用 SSH 以及之后如何通过 SSH 进入树莓派**_
在你可以用[树莓派][1]做的所有事情中,将其作为一个家庭网络的服务器是十分流行的做法。小体积与低功耗使它成为运行轻量级服务器的完美设备。
在这种情况下你做得到的事情之一是能够每次在树莓派上无须接上显示器、键盘、鼠标以及移动到放置你的树莓派的地方就可以运行指令。
你可以从其它任意电脑、笔记本、台式机甚至你的手机通过 SSH[Secure Shell][2])登入你的树莓派来做到这一点。让我展示给你看:
### 如何 SSH 进入树莓派
![][3]
我假设你已经[在你的树莓派上运行 Raspbian][4] 并已经成功通过有线或者无线网连进网络了。你的树莓派接入网络这点是很重要的否则你无法通过 SSH 连接树莓派(抱歉说出这种显而易见的事实)
### 步骤一:在树莓派上启用 SSH
SSH 在树莓派上是默认关闭的,因此在你安装好全新的 Raspbian 后打开树莓派时,你将不得不启用它。
首先通过菜单进入树莓派的配置界面。
![树莓派菜单,树莓派配置][5]
现在进入接口interfaces标签启动 SSH 并重启你的树莓派。
![在树莓派上启动 SSH][6]
你也可以通过终端直接启动 SSH。仅需输入命令_**sudo raspi-config**_ 然后进入高级设置以启用 SSH。
#### 步骤二: 找到树莓派的 IP 地址
在大多数情况下,你的树莓派会被分配一个看起来长得像**192.168.x.x**或者**10.x.x.x**的本地 IP 地址。你可以[使用多种 Linux 命令来找到 IP 地址][7]。
[][8]
推荐阅读《这款 Linux 恶意软件将矛头指向不安全的树莓派设备》
我在这使用古老而美好的 ifconfig 命令但是你也可以使用 _**ip address**_
```
ifconfig
```
![树莓派网络配置][9]
这行命令展现了所有活跃中的网络适配器以及其配置的列表。第一个条目(**eth0**)展示了例如**192.168.2.105**的有效 IP 地址。我用有线网将我的树莓派连入网络,因此这里显示的是 **eth0**。如果你用无线网的话在叫做 **wlan0** 的条目下查看。
你也可以用其他方法例如查看你的路由器或者调制解调器的网络设备表以找到 IP 地址。
#### 步骤三SSH 进你的树莓派
既然你已经启用了 SSH 功能并且找到了 IP 地址,你可以从任何电脑向前 SSH 进你的树莓派。你同样需要树莓派的用户名和密码。
默认用户名和密码是:
* username: pi
* password: raspberry
如果你已改变了默认的密码那就使用新的而不是以上的密码。理想状态下你必须改变默认的密码。在过去,一款[恶意软件感染数千使用默认用户名和密码的树莓派设备][8]。
(在 Mac 或 Linux 上)从你想要 SSH 进树莓派的电脑上打开终端输入以下命令,在 Windows 上,你可以用类似 [Putty][10] 的 SSH 客户端。
这里,使用你在之前步骤中找到的 IP 地址。
```
ssh [受保护的邮件]
```
_**注意: 确保你的树莓派和你用来 SSH 进入树莓派的电脑接入了同一个网络**_。
![通过命令行 SSH][11]
第一次你会看到一个警告,输入 **yes** 并按下回车。
![输入密码 \(默认是 raspberry\)][12]
现在,输入密码按下回车。
![成功通过 SSH 登入][13]
成功登入你将会看到树莓派的终端。现在你可以通过这个终端无序物理上访问你的树莓派就可以远程(在当前网络内)在它上面运行指令。
[][14]
推荐阅读《在低端系统上加速 Ubuntu Unity [快速指南]》
在此之上你也可以设置 SSH 密钥这样每次通过 SSH 登入时就可以无序输入密码,但那完全是另一个话题了。
我希望你通过跟着这个教程已能够 SSH 进入你的树莓派。在下方评论中让我知道你打算用你的树莓派做些什么!
--------------------------------------------------------------------------
via: https://itsfoss.com/ssh-into-raspberry/
作者:[Chinmay][a]
选题:[lujun9972][b]
译者:[tomjlw](https://github.com/tomjlw)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://itsfoss.com/author/chinmay/
[b]: https://github.com/lujun9972
[1]: https://www.raspberrypi.org/
[2]: https://en.wikipedia.org/wiki/Secure_Shell
[3]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/ssh-into-raspberry-pi.png?resize=800%2C450&ssl=1
[4]: https://itsfoss.com/tutorial-how-to-install-raspberry-pi-os-raspbian-wheezy/
[5]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/Raspberry-pi-configuration.png?ssl=1
[6]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/enable-ssh-raspberry-pi.png?ssl=1
[7]: https://linuxhandbook.com/find-ip-address/
[8]: https://itsfoss.com/raspberry-pi-malware-threat/
[9]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/ifconfig-rapberry-pi.png?ssl=1
[10]: https://itsfoss.com/putty-linux/
[11]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/SSH-into-pi-warning.png?fit=800%2C199&ssl=1
[12]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/SSH-into-pi-password.png?fit=800%2C202&ssl=1
[13]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/SSH-into-Pi-successful-login.png?fit=800%2C306&ssl=1
[14]: https://itsfoss.com/speed-up-ubuntu-unity-on-low-end-system/

View File

@ -0,0 +1,123 @@
[#]: collector: (lujun9972)
[#]: translator: (geekpi)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (PiShrink Make Raspberry Pi Images Smaller)
[#]: via: (https://www.ostechnix.com/pishrink-make-raspberry-pi-images-smaller/)
[#]: author: (sk https://www.ostechnix.com/author/sk/)
PiShrink - 使树莓派镜像更小
======
![Make Raspberry Pi Images Smaller With PiShrink In Linux][1]
**树莓派**不需要过多介绍。它是一款小巧、价格实惠,只有信用卡大小的电脑,它可以连接到显示器或电视。我们可以连接一个标准的键盘和鼠标,并将其用作一台成熟的台式计算机来完成日常任务,如互联网浏览、播放视频/玩游戏、文字处理和电子表格制作等。它主要是为学校的计算机科学教学而开发的。如今,树莓派被广泛用于大学、中小型组织和研究所来教授编码。如果你有一台树莓派,你可能需要了解一个名为 **“PiShrink”** 的 bash 脚本该脚本可使树莓派镜像更小。PiShrink 将自动缩小镜像,然后在启动时将其调整为 SD 卡的最大大小。这能更快地将镜像复制到 SD 卡中,同时缩小的镜像将更好地压缩。这对于将大容量镜像放入 SD 卡非常有用。在这个简短的指南中,我们将学习如何在类 Unix 系统中将树莓派镜像缩小到更小的大小。
### 安装 PiShrink
要在 Linux 机器上安装 PiShrink请先使用以下命令下载最新版本
```
$ wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
```
接下来,将下载的 PiShrink 变成二进制可执行文件:
```
$ chmod +x pishrink.sh
```
最后,移动到目录:
```
$ sudo mv pishrink.sh /usr/local/bin/
```
### 使树莓派镜像更小
你可能已经知道,**Raspbian** 是所有树莓派型号的官方操作系统。树莓派基金会为 PC 和 Mac 开发了**树莓派桌面**版本。你可以创建 live CD并在虚拟机中运行它甚至也可以将其安装在桌面上。树莓派也有少量非官方操作系统镜像。为了测试我从[**官方下载页面**][2]下载了官方的 Raspbian 系统。
解压下载的系统镜像:
```
$ unzip 2019-04-08-raspbian-stretch-lite.zip
```
上面的命令将提取当前目录中 **2019-04-08-raspbian-stretch-lite.zip** 文件的内容。
让我们看下提取文件的实际大小:
```
$ du -h 2019-04-08-raspbian-stretch-lite.img
1.7G 2019-04-08-raspbian-stretch-lite.img
```
如你所见,提取的树莓派系统镜像大小为 **1.7G**
现在,使用 PiShrink 缩小此文件的大小,如下所示:
```
$ sudo pishrink.sh 2019-04-08-raspbian-stretch-lite.img
```
示例输出:
```
Creating new /etc/rc.local
rootfs: 39795/107072 files (0.1% non-contiguous), 239386/428032 blocks
resize2fs 1.45.0 (6-Mar-2019)
resize2fs 1.45.0 (6-Mar-2019)
Resizing the filesystem on /dev/loop1 to 280763 (4k) blocks.
Begin pass 3 (max = 14)
Scanning inode table XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Begin pass 4 (max = 3728)
Updating inode references XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
The filesystem on /dev/loop1 is now 280763 (4k) blocks long.
Shrunk 2019-04-08-raspbian-stretch-lite.img from 1.7G to 1.2G
```
[![Make Raspberry Pi Images Smaller Using PiShrink][1]][3]
使用 PiShrink 使树莓派镜像更小
正如你在上面的输出中看到的,树莓派镜像的大小已减少到 **1.2G**
你还可以使用 **-s** 标志跳过该过程的自动扩展部分。
```
$ sudo pishrink.sh -s 2019-04-08-raspbian-stretch-lite.img newpi.img
```
这将创建一个源镜像文件(即 2019-04-08-raspbian-stretch-lite.img的副本到一个新镜像文件newpi.img并进行处理。有关更多详细信息请查看最后给出的官方 GitHub 页面。
就是这些了。希望本文有用。还有更多好东西,敬请期待!
**资源:**
* [**PiShrink 的 GitHub 仓库**][4]
* [**树莓派网站**][5]
--------------------------------------------------------------------------------
via: https://www.ostechnix.com/pishrink-make-raspberry-pi-images-smaller/
作者:[sk][a]
选题:[lujun9972][b]
译者:[geekpi](https://github.com/geekpi)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://www.ostechnix.com/author/sk/
[b]: https://github.com/lujun9972
[1]: https://www.ostechnix.com/wp-content/uploads/2019/05/pishrink-720x340.png
[2]: https://www.raspberrypi.org/downloads/
[3]: http://www.ostechnix.com/wp-content/uploads/2019/05/pishrink-1.png
[4]: https://github.com/Drewsif/PiShrink
[5]: https://www.raspberrypi.org/