mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-03-27 02:30:10 +08:00
Merge remote-tracking branch 'LCTT/master'
This commit is contained in:
commit
3859dd5ea3
@ -7,7 +7,7 @@
|
||||
[#]: via: (https://itsfoss.com/install-fedora-in-virtualbox/)
|
||||
[#]: author: (Dimitrios Savvopoulos https://itsfoss.com/author/dimitrios/)
|
||||
|
||||
如何在 Fedora 中安装 VirtualBox
|
||||
如何在 VirtualBox 中安装 Fedora
|
||||
======
|
||||
|
||||
如果你对 Fedora Linux 感兴趣,这有几种方式可以尝试它。
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
你甚至可以复制你的虚拟环境,并在其他系统上重新安装它。听起来方便吗?让我来教你怎么做。
|
||||
|
||||
### 在 Fedora 上安装 Fedora
|
||||
### 在 VirtualBox 上安装 Fedora
|
||||
|
||||
![][2]
|
||||
|
||||
|
@ -0,0 +1,115 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (A little bit of plain Javascript can do a lot)
|
||||
[#]: via: (https://jvns.ca/blog/2020/06/19/a-little-bit-of-plain-javascript-can-do-a-lot/)
|
||||
[#]: author: (Julia Evans https://jvns.ca/)
|
||||
|
||||
A little bit of plain Javascript can do a lot
|
||||
======
|
||||
|
||||
I’ve never worked as a professional frontend developer, so even though I’ve been writing HTML/CSS/JS for 15 years for little side projects, all of the projects have been pretty small, sometimes I don’t write any Javascript for years in between, and I often don’t quite feel like I know what I’m doing.
|
||||
|
||||
Partly because of that, I’ve leaned on libraries a lot! Ten years ago I used to use jQuery, and since maybe 2017 I’ve been using a lot of vue.js for my little Javascript projects (you can see a [little whack-a-mole game I made here as an intro to Vue][1]).
|
||||
|
||||
But last week, for the first time in a while, I wrote some plain Javascript without a library and it was fun so I wanted to talk about it a bit!
|
||||
|
||||
### experimenting with just plain Javascript
|
||||
|
||||
I really like Vue. But last week when I started building <https://questions.wizardzines.com>, I had slightly different constraints than usual – I wanted to use the same HTML to generate both a PDF (with [Prince][2]) and to make an interactive version of the questions.
|
||||
|
||||
I couldn’t really see how that would work with Vue (because Vue wants to create all the HTML itself), and because it was a small project I decided to try writing it in plain Javascript with no libraries – just write some HTML/CSS and add a single `<script src="js/script.js"> </script>`.
|
||||
|
||||
I hadn’t done this in a while, and I learned a few things along the way that made it easier than I thought it would be when I started.
|
||||
|
||||
### do almost everything by adding & removing CSS classes
|
||||
|
||||
I decided to implement almost all of the UI by just adding & removing CSS classes, and using [CSS transitions][3] if I want to animate a transition.
|
||||
|
||||
here’s a small example, where clicking the “next” question button adds the “done” class to the parent div.
|
||||
|
||||
```
|
||||
div.querySelector('.next-question').onclick = function () {
|
||||
show_next_row();
|
||||
this.parentElement.parentElement.classList.add('done');
|
||||
}
|
||||
```
|
||||
|
||||
This worked pretty well. My CSS as always is a bit of a mess but it felt manageable.
|
||||
|
||||
### add/remove CSS classes with `.classList`
|
||||
|
||||
I started out by editing the classes like this: `x.className = 'new list of classes'`. That felt a bit messy though and I wondered if there was a better way. And there was!
|
||||
|
||||
You can also add CSS classes like this:
|
||||
|
||||
```
|
||||
let x = document.querySelector('div');
|
||||
x.classList.add('hi');
|
||||
x.classList.remove('hi');
|
||||
```
|
||||
|
||||
`element.classList.remove('hi')` is way cleaner than what I was doing before.
|
||||
|
||||
### find elements with `document.querySelectorAll`
|
||||
|
||||
When I started learning jQuery I remember thinking that if you wanted to easily find something in the DOM you had to use jQuery (like `$('.class')`). I just learned this week that you can actually write `document.querySelectorAll('.some-class')` instead, and then you don’t need to depend on any library!
|
||||
|
||||
I got curious about when `querySelectorAll` was introduced. I Googled a tiny bit and it looks like the [Selectors API was built sometime between 2008 and 2013 – I found a [post from the jQuery author discussing the proposed implementation in 2008][4], and [a blog post from 2011][5] saying it was in all major browsers by then, so maybe it didn’t exist when I started using jQuery but it’s definitely been around for quite a while :)
|
||||
|
||||
### set `.innerHTML`
|
||||
|
||||
In one place I wanted to change a button’s HTML contents. Creating DOM elements with `document.createElement` is pretty annoying, so I tried to do that as little as possible and instead set `.innerHTML` to the HTML string I wanted:
|
||||
|
||||
```
|
||||
button.innerHTML = `<i class="icon-lightbulb"></i>I learned something!
|
||||
<object data="/confetti.svg" width="30" height = "30"> </object>
|
||||
`;
|
||||
```
|
||||
|
||||
### scroll through the page with `.scrollIntoView`
|
||||
|
||||
The last fun thing I learned about is `.scrollIntoView` – I wanted to scroll down to the next question automatically when someone clicked “next question”. Turns out this is just one line of code:
|
||||
|
||||
```
|
||||
row.classList.add('revealed');
|
||||
row.scrollIntoView({behavior: 'smooth', block: 'center'});
|
||||
```
|
||||
|
||||
### another vanilla JS example: peekobot
|
||||
|
||||
Another small example of a plain JS library I thought was nice is [peekobot][6], which is a little chatbot interface that’s 100 lines of JS/CSS.
|
||||
|
||||
Looking at [its Javascript][7], it uses some similar patterns – a lot of `.classList.add`, some adding elements to the DOM, some `.querySelectorAll`.
|
||||
|
||||
I learned from reading peekobot’s source about [.closest][8] which finds the closest ancestor that matches a given selector. That seems like it would be a nice way to get rid of some of the `.parentElement.parentElement` that I was writing in my Javascript, which felt a bit fragile.
|
||||
|
||||
### plain Javascript can do a lot!
|
||||
|
||||
I was pretty surprised by how much I could get done with just plain JS. I ended up writing about 50 lines of JS to do everything I wanted to do, plus a bit extra to collect some anonymous metrics about what folks were learning.
|
||||
|
||||
As usual with my frontend posts, this isn’t meant to be Serious Frontend Engineering Advice – my goal is to be able to write little websites with less than 200 lines of Javascript that mostly work. If you are also flailing around in frontend land I hope this helps a bit!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://jvns.ca/blog/2020/06/19/a-little-bit-of-plain-javascript-can-do-a-lot/
|
||||
|
||||
作者:[Julia Evans][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://jvns.ca/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://jvns.ca/blog/2017/06/26/vue-js-fun/
|
||||
[2]: https://www.princexml.com/
|
||||
[3]: https://3dtransforms.desandro.com/card-flip
|
||||
[4]: https://johnresig.com/blog/thoughts-on-queryselectorall/
|
||||
[5]: https://tiffanybbrown.com/2011/08/12/meet-the-selectors-api/
|
||||
[6]: https://peekobot.github.io/peekobot/
|
||||
[7]: https://github.com/Peekobot/peekobot/blob/master/peekobot.js
|
||||
[8]: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
|
@ -0,0 +1,75 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (scanimage: scan from the command line!)
|
||||
[#]: via: (https://jvns.ca/blog/2020/07/11/scanimage--scan-from-the-command-line/)
|
||||
[#]: author: (Julia Evans https://jvns.ca/)
|
||||
|
||||
scanimage: scan from the command line!
|
||||
======
|
||||
|
||||
Here’s another quick post about a command line tool I was delighted by.
|
||||
|
||||
Last night, I needed to scan some documents for some bureaucratic reasons. I’d never used a scanner on Linux before and I was worried it would take hours to figure out. I started by using `gscan2pdf` and had trouble figuring out the user interface – I wanted to scan both sides of the page at the same time (which I knew our scanner supported) but couldn’t get it to work.
|
||||
|
||||
### enter scanimage!
|
||||
|
||||
`scanimage` is a command line tool, in the `sane-utils` Debian package. I think all Linux scanning tools use the `sane` libraries (“scanner access now easy”) so my guess is that it has similar abilities to any other scanning software. I didn’t need OCR in this case so we’re not going to talk about OCR.
|
||||
|
||||
### get your scanner’s name with `scanimage -L`
|
||||
|
||||
`scanimage -L` lists all scanning devices you have.
|
||||
|
||||
At first I couldn’t get this to work and I was a bit frustrated but it turned out that I’d connected the scanner to my computer, but not plugged it into the wall. Oops.
|
||||
|
||||
Once everything was plugged in it worked right away. Apparently our scanner is called `fujitsu:ScanSnap S1500:2314`. Hooray!
|
||||
|
||||
### list options for your scanner with `--help`
|
||||
|
||||
Apparently each scanner has different options (makes sense!) so I ran this command to get the options for my scanner:
|
||||
|
||||
```
|
||||
scanimage --help -d 'fujitsu:ScanSnap S1500:2314'
|
||||
```
|
||||
|
||||
I found out that my scanner supported a `--source` option (which I could use to enable duplex scanning) and a `--resolution` option (which I changed to 150 to decrease the file sizes and make scanning faster).
|
||||
|
||||
### scanimage doesn’t output PDFs (but you can write a tiny script)
|
||||
|
||||
The only downside was – I wanted a PDF of my scanned document, and scanimage doesn’t seem to support PDF output.
|
||||
|
||||
So I wrote this 5-line shell script to scan a bunch of PNGs into a temp directory and convert the resulting PNGs to a PDF.
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
DIR=`mktemp -d`
|
||||
CUR=$PWD
|
||||
cd $DIR
|
||||
scanimage -b --format png -d 'fujitsu:ScanSnap S1500:2314' --source 'ADF Front' --resolution 150
|
||||
convert *.png $CUR/$1
|
||||
```
|
||||
|
||||
I ran the script like this. `scan-single-sided output-file-to-save.pdf`
|
||||
|
||||
You’ll probably need a different `-d` and `--source` for your scanner.
|
||||
|
||||
### it was so easy!
|
||||
|
||||
I always expect using printers/scanners on Linux to be a nightmare and I was really surprised how `scanimage` Just Worked – I could just run my script with `scan-single-sided receipts.pdf` and it would scan a document and save it to `receipts.pdf`!.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://jvns.ca/blog/2020/07/11/scanimage--scan-from-the-command-line/
|
||||
|
||||
作者:[Julia Evans][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://jvns.ca/
|
||||
[b]: https://github.com/lujun9972
|
@ -1,133 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (5 new sudo features you need to know in 2020)
|
||||
[#]: via: (https://opensource.com/article/20/10/sudo-19)
|
||||
[#]: author: (Peter Czanik https://opensource.com/users/czanik)
|
||||
|
||||
5 new sudo features you need to know in 2020
|
||||
======
|
||||
From central session recording through chroot support to Python API,
|
||||
sudo 1.9 offers many new features.
|
||||
![Wratchet set tools][1]
|
||||
|
||||
When you want to perform an action on a [POSIX system][2], one of the safest ways to do so is to use the sudo command. Unlike logging in as the root user and performing what could be a dangerous action, sudo grants any user [designated as a "sudoer"][3] by the sysadmin temporary permission to perform a normally restricted activity.
|
||||
|
||||
This system has helped keep Linux, Unix, and macOS systems safe from silly mistakes and malicious attacks for decades, and it is the default administrative mechanism on all major Linux distributions today.
|
||||
|
||||
When it was released in May 2020, sudo 1.9 brought many new features, including central collection of session recordings, support for chroot within sudo, and a Python API. If you are surprised by any of these, read my article about some [lesser-known features of sudo][4].
|
||||
|
||||
Sudo is a lot more than just a prefix for administrative commands. You can fine-tune permissions, record what is happening on the terminal, extend sudo using plugins, store configurations in LDAP, do extensive logging, and much more.
|
||||
|
||||
Version 1.9.0 and subsequent minor releases added a variety of new features (which I'll describe below), including:
|
||||
|
||||
* A recording service to collect sudo session recordings centrally
|
||||
* Audit plugin API
|
||||
* Approval plugin API
|
||||
* Python support for plugins
|
||||
* Chroot and CWD support built into sudo (starting with 1.9.3)
|
||||
|
||||
|
||||
|
||||
### Where to get sudo 1.9
|
||||
|
||||
Most Linux distributions still package the previous generation of sudo (version 1.8), and it will stay that way in long-term support (LTS) releases for several years. The most complete sudo 1.9 package I am aware of in a Linux distribution is openSUSE [Tumbleweed][5], which is a rolling distro, and the sudo package has Python support available in a subpackage. Recent [Fedora][6] releases include sudo 1.9 but without Python. [FreeBSD Ports][7] has the latest sudo version available, and you can enable Python support if you build sudo yourself instead of using the package.
|
||||
|
||||
If your favorite Linux distribution does not yet include sudo 1.9, check the [sudo binaries page][8] to see if a ready-to-use package is available for your system. This page also has packages for several commercial Unix variants.
|
||||
|
||||
As usual, before you start experimenting with sudo settings, _make sure you know the root password_. Yes, even on Ubuntu. Having a temporary "backdoor" is important; without it, you would have to hack your own system if something goes wrong. And remember: a syntactically correct configuration does not mean that anybody can do anything through sudo on that system!
|
||||
|
||||
### Recording service
|
||||
|
||||
The recording service collects session recordings centrally. This offers many advantages compared to local session log storage:
|
||||
|
||||
* It is more convenient to search in one place instead of visiting individual machines for recordings
|
||||
* Recordings are available even if the sending machine is down
|
||||
* Recordings cannot be deleted by local users who want to cover their tracks
|
||||
|
||||
|
||||
|
||||
For a quick test, you can send sessions through non-encrypted connections to the recording service. My blog contains [instructions][9] for setting it up in just a few minutes. For a production setup, I recommend using encryption. There are many possibilities, so read the [documentation][10] that best suits your environment.
|
||||
|
||||
### Audit plugin API
|
||||
|
||||
The new audit plugin API is not a user-visible feature. In other words, you cannot configure it from the sudoers file. It is an API, meaning that you can access audit information from plugins, including ones written in Python. You can use it in many different ways, like sending events from sudo directly to Elasticsearch or Logging-as-a-Service (LaaS) when something interesting happens. You can also use it for debugging and print otherwise difficult-to-access information to the screen in whatever format you like.
|
||||
|
||||
Depending on how you want to use it, you can find its documentation in the sudo plugin manual page (for C) and the sudo Python plugin manual. [Sample Python code][11] is available in the sudo source code, and there is also a [simplified example][12] on my blog.
|
||||
|
||||
### Approval plugin API
|
||||
|
||||
The approval plugin API makes it possible to include extra restrictions before a command will execute. These will run only after the policy plugin succeeds, so you can effectively add additional policy layers without replacing the policy plugin and thus sudoers. Multiple approval plugins may be defined, and all must succeed for the command to execute.
|
||||
|
||||
As with the audit plugin API, you can use it both from C and Python. The [sample Python code][13] documented on my blog is a good introduction to the API. Once you understand how it works, you can extend it to connect sudo to ticketing systems and approve sessions only with a related open ticket. You can also connect to an HR database so that only the engineer on duty can gain administrative privileges.
|
||||
|
||||
### Python support for plugins
|
||||
|
||||
Even though I am not a programmer, my favorite new sudo 1.9 feature is Python support for plugins. You can use most of the APIs available from C with Python as well. Luckily, sudo is not performance-sensitive, so the relatively slow speed of running Python code is not a problem for sudo. Using Python for extending sudo has many advantages:
|
||||
|
||||
* Easier, faster development
|
||||
* No need to compile; code might even be distributed by configuration management
|
||||
* Many APIs do not have ready-to-use C clients, but Python code is available
|
||||
|
||||
|
||||
|
||||
In addition to the audit and approval plugin APIs, there are a few others available, and you can do very interesting things with them.
|
||||
|
||||
By using the policy plugin API, you can replace the sudo policy engine. Note you will lose most sudo features, and there is no more sudoers-based configuration. This can still be useful in niche cases, but most of the time, it is better to keep using sudoers and create additional policies using the approval plugin API. If you want to give it a try, my [introduction to the Python plugin][14] provides a very simple policy: allowing only the `id` command. Once again, make sure you know the root password, as once this policy is enabled, it prevents any practical use of sudo.
|
||||
|
||||
Using the I/O logs API, you can access input and output from user sessions. This means you can analyze what is happening in a session and even terminate it if you find something suspicious. This API has many possible uses, such as data-leak prevention. You can monitor the screen for keywords and, if any of them appear in the data stream, you can break the connection before the keyword can appear on the user's screen. Another possibility is checking what the user is typing and using that data to reconstruct the command line the user is entering. For example, if a user enters `rm -fr /`, you can disconnect the user even before Enter is hit.
|
||||
|
||||
The group plugin API allows non-Unix group lookups. In a way, this is similar to the approval plugin API as it also extends the policy plugin. You can check if a user is part of a given group and act based on this in later parts of the configuration.
|
||||
|
||||
### Chroot and CWD support
|
||||
|
||||
The latest additions to sudo are chroot and change working directory (CWD) support. Neither option is enabled by default—you need to explicitly enable them in the sudoers file. When they're enabled, you can fine-tune target directories or allow users to specify which directory to use. The logs reflect when these settings were used.
|
||||
|
||||
On most systems, chroot is available only to root. If one of your users needs chroot, you need to give them root access, which gives them a lot more power than just chroot. Alternately, you can allow access to the chroot command through sudo, but it still allows loopholes where they can gain full access. When you use sudo's built-in chroot support, you can easily restrict access to a single directory. You can also give users the flexibility to specify the root directory. Of course, this might lead to disasters (e.g., `sudo --chroot / -s`), but at least the event is logged.
|
||||
|
||||
When you run a command through sudo, it sets the working directory to the current directory. This is the expected behavior, but there may be cases when the command needs to be run in a different directory. For example, I recall using an application that checked my privileges by checking whether my working directory was `/root`.
|
||||
|
||||
### Try the new features
|
||||
|
||||
I hope that this article inspires you to take a closer look at sudo 1.9. Central session recording is both more convenient and secure than storing session logs locally. Chroot and CWD support give you additional security and flexibility. And using Python to extend sudo makes it easy to custom-tailor sudo to your environment. You can try the new features by using one of the latest Linux distributions or the ready-to-use packages from the sudo website.
|
||||
|
||||
If you want to learn more about sudo, here are a few resources:
|
||||
|
||||
* [Sudo website][15]
|
||||
* [Sudo blog][16]
|
||||
* [Sudo on Twitter][17]
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/10/sudo-19
|
||||
|
||||
作者:[Peter Czanik][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/czanik
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/tools_osyearbook2016_sysadmin_cc.png?itok=Y1AHCKI4 (Wratchet set tools)
|
||||
[2]: https://opensource.com/article/19/7/what-posix-richard-stallman-explains
|
||||
[3]: https://opensource.com/article/17/12/using-sudo-delegate
|
||||
[4]: https://opensource.com/article/19/10/know-about-sudo
|
||||
[5]: https://software.opensuse.org/distributions/tumbleweed
|
||||
[6]: https://getfedora.org/
|
||||
[7]: https://www.freebsd.org/ports/
|
||||
[8]: https://www.sudo.ws/download.html#binary
|
||||
[9]: https://blog.sudo.ws/posts/2020/03/whats-new-in-sudo-1.9-recording-service/
|
||||
[10]: https://www.sudo.ws/man/sudo_logsrvd.man.html#EXAMPLES
|
||||
[11]: https://github.com/sudo-project/sudo/blob/master/plugins/python/example_audit_plugin.py
|
||||
[12]: https://blog.sudo.ws/posts/2020/06/sudo-1.9-using-the-new-audit-api-from-python/
|
||||
[13]: https://blog.sudo.ws/posts/2020/08/sudo-1.9-using-the-new-approval-api-from-python/
|
||||
[14]: https://blog.sudo.ws/posts/2020/01/whats-new-in-sudo-1.9-python/
|
||||
[15]: https://www.sudo.ws/
|
||||
[16]: https://blog.sudo.ws/
|
||||
[17]: https://twitter.com/sudoproject
|
@ -0,0 +1,119 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Day 2: Rails associations & dragging divs around)
|
||||
[#]: via: (https://jvns.ca/blog/2020/11/10/day-2--rails-associations---dragging-divs-around/)
|
||||
[#]: author: (Julia Evans https://jvns.ca/)
|
||||
|
||||
Day 2: Rails associations & dragging divs around
|
||||
======
|
||||
null
|
||||
Hello! Today was day 2 of building my toy project. Here are a few more notes on things that have been fun about Rails!
|
||||
|
||||
### the goal: make a refrigerator poetry forum
|
||||
|
||||
I wanted to make kind of a boring standard website to learn Rails, and that other people could interact with. Like a forum! But of course if people can actually type on a website that creates all kinds of problems (what if they’re spammers? or just mean?).
|
||||
|
||||
The first idea I came up with that would let people interact with the website but not actually be able to type things into it was a refrigerator poetry forum where you can write poems given only a fixed set of words.
|
||||
|
||||
So, that’s the plan!
|
||||
|
||||
My goal with this project is to find out if I want to use Rails for other small web projects (instead of what I usually do, which is use something more basic like Flask, or give up on having a backend at all and write everything in Javascript).
|
||||
|
||||
### how do you drag the words around? jQuery UI draggable!
|
||||
|
||||
I wanted people to be able to drag the words around, but I didn’t feel like writing a lot of Javascript. It turns out that this is SUPER easy – there’s a jQuery library to do it called “draggable”!
|
||||
|
||||
At first the dragging didn’t work on mobile, but there’s a hack to make jQuery UI work on mobile called [jQuery UI touch punch][1]. Here’s what it looks like (you can view source if you’re interested in seeing how it works, there’s very little code).
|
||||
|
||||
banana forest cake is
|
||||
|
||||
### a fun Rails feature: “associations”
|
||||
|
||||
I’ve never used a relational ORM before, and one thing I was excited about with Rails was to see what using Active Record is like! Today I learned about one of Rails’ ORM features: [associations][2]. Here’s what that’s about if you know absolutely nothing about ORMs like me.
|
||||
|
||||
In my forum, I have:
|
||||
|
||||
* users
|
||||
* topics (I was going to call this “threads” but apparently that’s a reserved word in Rails so they’re called “topics” for now)
|
||||
* posts
|
||||
|
||||
|
||||
|
||||
When displaying a post, I need to show the username of the user who created the post. So I thought I might need to write some code to load the posts and load the user for each post like this: (in Rails, `Post.where` and `User.find` will run SQL statements and turn the results into Ruby objects)
|
||||
|
||||
```
|
||||
@posts = Post.where(topic_id: id)
|
||||
@posts.each do |post|
|
||||
user = User.find(post.user_id)
|
||||
post.user = user
|
||||
end
|
||||
```
|
||||
|
||||
This is no good though – it’s doing a separate SQL query for every post! I knew there was a better way, and I found out that it’s called [Associations][2]. That link is to the guide from <https://guides.rubyonrails.org>, which has treated me well so far.
|
||||
|
||||
Basically all I needed to do was:
|
||||
|
||||
1. Add a `has_many :posts` line to the User model
|
||||
2. Add a `belongs_to :user` line to the Post model
|
||||
3. Rails now knows how to join these two tables even though I didn’t tell it what columns to join on! I think this is because I named the `user_id` column in the `posts` table according to the convention it expects.
|
||||
4. Do the exact same thing for `User` and `Topic` (a topic also `has_many :posts`)
|
||||
|
||||
|
||||
|
||||
And then my code to load every post along with its associated user becomes just one line! Here’s the line:
|
||||
|
||||
```
|
||||
@posts = @topic.posts.order(created_at: :asc).preload(:user)
|
||||
```
|
||||
|
||||
More importantly than it being just one line, instead of doing a separate query to get the user for each post, it gets all the users in 1 query. Apparently there are a bunch of [different ways][3] to do similar things in Rails (preload, eager load, joins, and includes?). I don’t know what all those are yet but maybe I’ll learn that later.
|
||||
|
||||
### a fun Rails feature: scaffolding!
|
||||
|
||||
Rails has this command line tool called `rails` and it does a lot of code generation. For example, I wanted to add a Topic model / controller. Instead of having to go figure out where to add all the code, I could just run:
|
||||
|
||||
```
|
||||
rails generate scaffold Topic title:text
|
||||
```
|
||||
|
||||
and it generated a bunch of code, so that I already had basic endpoints to create / edit / delete Topics. For example, here’s my [topic controller right now][4], most of which I did not write (I only wrote the highlighted 3 lines). I’ll probably delete a lot of it, but it feels kinda nice to have a starting point where I can expand on the parts I want and delete the parts I don’t want.
|
||||
|
||||
### database migrations!
|
||||
|
||||
The `rails` tool can also generate database migrations! For example, I decided I wanted to remove the `title` field from posts.
|
||||
|
||||
Here’s what I had to do:
|
||||
|
||||
```
|
||||
rails generate migration RemoveTitleFromPosts title:string
|
||||
rails db:migrate
|
||||
```
|
||||
|
||||
That’s it – just run a couple of command line incantations! I ran a few of these migrations as I changed my mind about what I wanted my database schema to be and it’s been pretty straightforward so far – it feels pretty magical.
|
||||
|
||||
It got a tiny bit more interesting when I tried to add a `not null` constraint to a column where some of the fields in that column were null – the migration failed. But I could just fix the offending records and easily rerun the migration.
|
||||
|
||||
### that’s all for today!
|
||||
|
||||
tomorrow maybe I’ll put it on the internet if I make more progress.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://jvns.ca/blog/2020/11/10/day-2--rails-associations---dragging-divs-around/
|
||||
|
||||
作者:[Julia Evans][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://jvns.ca/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://github.com/furf/jquery-ui-touch-punch
|
||||
[2]: https://guides.rubyonrails.org/association_basics.html
|
||||
[3]: https://blog.bigbinary.com/2013/07/01/preload-vs-eager-load-vs-joins-vs-includes.html
|
||||
[4]: https://github.com/jvns/refrigerator-forum/blob/776b3227cfd7004cb1efb00ec7e3f82a511cbdc4/app/controllers/topics_controller.rb#L13-L15
|
46
sources/tech/20201111 Day 3- an infinitely tall fridge.md
Normal file
46
sources/tech/20201111 Day 3- an infinitely tall fridge.md
Normal file
@ -0,0 +1,46 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Day 3: an infinitely tall fridge)
|
||||
[#]: via: (https://jvns.ca/blog/2020/11/11/day-3--an-infinitely-tall-fridge/)
|
||||
[#]: author: (Julia Evans https://jvns.ca/)
|
||||
|
||||
Day 3: an infinitely tall fridge
|
||||
======
|
||||
|
||||
Hello! Here are some notes from Day 3 at the Recurse Center.
|
||||
|
||||
This post is an extremely short one from the toy refrigerator poetry forum website I’m working on. I needed to come up with a design for it, and finally today I came up with an idea: just put everything on an image of a fridge.
|
||||
|
||||
I found a stock image of a fridge, but I ran into a problem immediately, which was that the entire website could not fit on said fridge image.
|
||||
|
||||
So I figured how to make a fridge that was as tall as I wanted it to be. (not technically “infinite”, but “a fridge that is as big as required” didn’t have quite the same ring).
|
||||
|
||||
### here’s the infinite fridge
|
||||
|
||||
Here’s a CodePen with the HTML/CSS required to make an infinite fridge. It’s relatively simple and I’m very pleased about this. It basically has 3 images: one for the top of the fridge, a 1px line that can be repeated as much as required, and then the bottom.
|
||||
|
||||
See the Pen [infinite refrigerator][1] by Julia Evans ([Julia Evans][2]) on [CodePen][3].
|
||||
|
||||
### that’s all!
|
||||
|
||||
I started writing an explanation of how exactly this infinite fridge works, but I ran out of time so maybe another day :). (the main trick is that `padding-bottom` is a percentage of the parent element’s width, not its height, so you can use it to create a box with a fixed aspect ratio)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://jvns.ca/blog/2020/11/11/day-3--an-infinitely-tall-fridge/
|
||||
|
||||
作者:[Julia Evans][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://jvns.ca/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://codepen.io/wizardzines/pen/bGeOdvJ
|
||||
[2]: https://codepen.io/wizardzines
|
||||
[3]: https://codepen.io
|
@ -1,102 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (LazPaint: A Free & Open Source Paint.NET Alternative)
|
||||
[#]: via: (https://itsfoss.com/lazpaint/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
LazPaint: A Free & Open Source Paint.NET Alternative
|
||||
======
|
||||
|
||||
_**Brief: LazPaint is an open-source Paint.NET alternative with cross-platform support. It is a lightweight program with a bunch of essential options to edit images quickly. Here’s an overview of LazPaint.**_
|
||||
|
||||
### LazPaint: Open Source Paint.NET Alternative for Linux
|
||||
|
||||
![][1]
|
||||
|
||||
If you are fond of using tools to quickly edit and manipulate images and screenshots, you may have heard about [Paint.NET][2], which is available only for Windows systems.
|
||||
|
||||
It is a popular nifty tool to get a lot of basic editing tasks done along with a bunch of options available. You might be aware of several [ima][3][ge editing tools][3] but Paint.NET is a pretty popular option just because it is easy to use without any bloated feature for an average user.
|
||||
|
||||
[LazPaint][4] comes to the rescue as an impressive open source replacement to Paint.NET for Linux, Windows, and macOS. It offers most of the essential features one would need to manipulate images while being easy to use.
|
||||
|
||||
![][5]
|
||||
|
||||
Since it is cross-platform application, even if you’re not using a Linux system, you could still start using it as a no-nonsense free and open-source tool, if that’s your priority. Now, let us take a look at some features it offers.
|
||||
|
||||
### Features of LazPaint
|
||||
|
||||
![][6]
|
||||
|
||||
As I mentioned earlier, LazPaint offers a bunch of essential features. Here, I’ll list the key highlights that can help you decide if you need it. However, I’d recommend you to explore it to know more about it.
|
||||
|
||||
* All major file formats supported (including layered Bitmaps and 3D files)
|
||||
* Selection Tools, Crop to Selection, Selection Pen, Invert Selection
|
||||
* Export to [Krita][7] supported
|
||||
* Image resampling with various quality settings
|
||||
* Motion Blur, Custom Blur, Radial Blur, and Pixelate tool
|
||||
* Ability to remove transparency and flatten the image
|
||||
* Rotate and Flip images
|
||||
* Convert images to Negatives
|
||||
* Ability to re-size Canvas
|
||||
* Deforming tool (perspective)
|
||||
* Advanced Drawing tools
|
||||
* Set workspace color
|
||||
* Dark theme
|
||||
* Supports script functionality
|
||||
* Layer support with essential management options
|
||||
* Layer effects
|
||||
* Filters
|
||||
* Grayscale effect
|
||||
* Ability to enable/disable the toolbar in or attach them to the Dock
|
||||
|
||||
|
||||
|
||||
**Recommended Read:**
|
||||
|
||||
![][8]
|
||||
|
||||
#### [Drawing is an Open Source MS-Paint Type of App for Linux Desktop][9]
|
||||
|
||||
### Installing LazPaint on Linux
|
||||
|
||||
You should find it available in your official repositories to install it via your default package manager but to get the latest version, you will have to download the .**deb** file or compile it from source on non-Debian based Distributions.
|
||||
|
||||
I wish there was a Flatpak available to get the latest version on every Linux distribution — but nothing as of now.
|
||||
|
||||
It is available for Windows and macOS as well. You will also find a portable version available for Windows, that could come in handy.
|
||||
|
||||
[LazPaint][4]
|
||||
|
||||
### Closing Thoughts on LazPaint
|
||||
|
||||
I found it really easy to use and the variety of quality settings to re-sample (or resize) an image is definitely a good addition. If you’ve already installed it, you must have noticed that it does not take a significant amount of storage to get installed and is a lightweight program overall.
|
||||
|
||||
It’s snappy, and most of the functions that I tested in my quick usage worked quite well without any issues.
|
||||
|
||||
What do you think about LazPaint as a Paint.NET alternative? Let me know your thoughts in the comments below.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/lazpaint/
|
||||
|
||||
作者:[Ankush Das][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/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/11/lazpaint.jpg?resize=800%2C397&ssl=1
|
||||
[2]: https://www.getpaint.net
|
||||
[3]: https://itsfoss.com/image-applications-ubuntu-linux/
|
||||
[4]: https://lazpaint.github.io/
|
||||
[5]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/11/lazpaint-screenshot-2.png?resize=800%2C481&ssl=1
|
||||
[6]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/11/lazpaint-screenshot.jpg?resize=800%2C484&ssl=1
|
||||
[7]: https://krita.org/en
|
||||
[8]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/09/drawing-app-interface.jpg?fit=789%2C449&ssl=1
|
||||
[9]: https://itsfoss.com/drawing-app/
|
@ -0,0 +1,152 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (6 predictions for JavaScript build tools)
|
||||
[#]: via: (https://opensource.com/article/20/11/javascript-build-tools)
|
||||
[#]: author: (Shedrack Akintayo https://opensource.com/users/shedrack-akintayo)
|
||||
|
||||
6 predictions for JavaScript build tools
|
||||
======
|
||||
The JavaScript frontend tooling ecosystem is dynamic and competitive,
|
||||
and only the best survive.
|
||||
![Magnifying glass on code][1]
|
||||
|
||||
Code used in production is different from development code. In production, you need to build packages that run fast, manage dependencies, automate tasks, load external modules, and more. [JavaScript][2] tools that make it possible to turn development code into production code are called _build tools._
|
||||
|
||||
The reason frontend code is "built" can be explained by looking into the various build steps and their importance.
|
||||
|
||||
### Steps in building frontend code
|
||||
|
||||
There are four steps involved in building frontend code:
|
||||
|
||||
#### 1\. Transpiling
|
||||
|
||||
Transpiling lets developers take advantage of the newest, hottest updates to languages and extensions and maintain browser compatibility. Here is an example using [Babel][3]:
|
||||
|
||||
|
||||
```
|
||||
// arrow function syntax in array map
|
||||
const double = [1, 2, 3].map((num) => num * 2);
|
||||
// after being transpiled
|
||||
const double = [1, 2, 3].map(function(num) {
|
||||
return num * 2;
|
||||
});
|
||||
```
|
||||
|
||||
#### 2\. Bundling
|
||||
|
||||
Bundling is the process of taking all the "import" or "require" statements; finding the matching JavaScript snippets, packages, and libraries; adding them to the appropriate scope; and packaging it all into one big JavaScript file. Commonly used bundlers include Browserify, Webpack, and Parcel.
|
||||
|
||||
#### 3\. Minifing
|
||||
|
||||
Minifying reduces the final file size by removing white space and code comments. You can take this a step further by adding an obfuscate step, which changes variable names and method names, obscuring the code, so it is less human-readable once it's delivered to the client. Here is an example using Grunt:
|
||||
|
||||
|
||||
```
|
||||
// before minifying
|
||||
const double = [1, 2, 3].map(function(num) {
|
||||
return num * 2;
|
||||
});
|
||||
// after minifying
|
||||
const double = [1, 2, 3].map(function(num) {
|
||||
return num * 2;
|
||||
});
|
||||
```
|
||||
|
||||
#### 4\. Packaging
|
||||
|
||||
After all the steps above are finished, the compatible, bundled, minified/obfuscated file must be put somewhere. Packaging is the process of putting the results of the above steps somewhere specified by the developer. This is usually done by the bundler.
|
||||
|
||||
### Frontend build tools
|
||||
|
||||
Frontend tooling and build tools can be split into the following categories:
|
||||
|
||||
* Package managers: NPM, Yarn
|
||||
* Transpilers: Babel, etc.
|
||||
* Bundlers: Webpack, Parcel, Browserify
|
||||
* Minifiers: UglifyJS, Packer, Minify, etc.
|
||||
|
||||
|
||||
|
||||
There are a variety of build tools you can use in the JavaScript ecosystem, including the following.
|
||||
|
||||
#### Grunt and Bower
|
||||
|
||||
[Grunt][4] was introduced as a command-line tool that provided just one script to specify and configure tasks. [Bower][5] followed shortly after as a way to manage client-side packages. The two, along with NPM, seemed to fulfill the majority of automation needs and were used regularly together. The problem with Grunt was that it didn't provide developers the freedom to configure more complex task chains, while Bower made developers manage twice as many packages as usual because it separates frontend and backend packages (i.e., Bower components vs. Node modules).
|
||||
|
||||
**The future of Grunt and Bower:** Grunt and Bower are on their way out of the JavaScript tooling ecosystem, but there are several replacements.
|
||||
|
||||
#### Gulp and Browserify
|
||||
|
||||
[Gulp][6] was released a year and a half after Grunt. It felt natural. Writing a build script in JavaScript compared to JSON gave freedom. You could write functions, create variables on the fly, use conditionals anywhere—not that this would make for a particularly good-looking build script, but it was possible. [Browserify][7] and Gulp can be used in tandem. Browserify allows NPM packages (which are for backend Node servers) to be brought into the frontend, making Bower obsolete. It also looks and feels better, with one package manager for the frontend and backend.
|
||||
|
||||
**The future of Gulp:** Gulp can be improved to match the current popular build tools, but this is entirely up to the creators. It is still in use but not as popular as it was before.
|
||||
|
||||
#### Webpack and NPM/Yarn scripts
|
||||
|
||||
[Webpack][8] is the hottest kid on the block in modern frontend development tooling. Webpack is an open-source JavaScript module bundler. It is made primarily for JavaScript, but it can transform frontend assets like HTML, CSS, and images if the corresponding loaders are included. With Webpack, you can also write scripts like Gulp and execute them with [NPM/Yarn][9].
|
||||
|
||||
**The future of Webpack:** Webpack is currently the hottest thing in the JavaScript tooling ecosystem, and all the JS cool kids are using React and Webpack these days. It is currently in version 4 and not going anywhere anytime soon.
|
||||
|
||||
#### Parcel
|
||||
|
||||
[Parcel][10] is a web application bundler that launched in 2018 and is differentiated by its developer experience. It offers blazing-fast performance utilizing multicore processing and requires zero configuration. Parcel is also a new kid on the block, but adoption hasn't been fast, especially for large applications. Developers prefer to use Webpack over Parcel because of the former's extensive support and customizability.
|
||||
|
||||
**The future of Parcel:** Parcel is very easy to use, it is faster than Webpack if you measure bundle and build time, and it also offers a better developer experience. The reason Parcel hasn't been adopted much might be because it's still relatively new. Parcel has a very bright future in the frontend build tools ecosystem, and it will be around for a while.
|
||||
|
||||
#### Rollup
|
||||
|
||||
[Rollup][11] is a module bundler for JavaScript that compiles small pieces of code into something larger and more complex, such as a library or an application. It is advisable to use Rollup when building a library with minimal third-party imports.
|
||||
|
||||
**The future of Rollup:** Rollup is super-cool and is being adopted rapidly. It has great features and will be part of the frontend tooling ecosystem for a long time.
|
||||
|
||||
### Learn more
|
||||
|
||||
The JavaScript tooling ecosystem is dynamic and competitive, and only the best tools survive. In the future, we will see tools that require less (or no) configuration, better customizability, more extensibility, and higher speed.
|
||||
|
||||
The tools you use for an application's frontend are a personal call that you need to make based on your project's requirements. Choose what works best for you, and most of the time, there are tradeoffs.
|
||||
|
||||
For more information, see:
|
||||
|
||||
* [JavaScript tooling: The evolution and future of JS/frontend build tools][12]
|
||||
* [Tools and modern workflow for frontend developers][13]
|
||||
* [Modern frontend: The tools and build process explained][14]
|
||||
* [Best build tools in frontend development][15]
|
||||
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
_This article originally appeared on [Shedrack Akintayo's blog][16] and is republished with his permission._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/11/javascript-build-tools
|
||||
|
||||
作者:[Shedrack Akintayo][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/shedrack-akintayo
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/find-file-linux-code_magnifying_glass_zero.png?itok=E2HoPDg0 (Magnifying glass on code)
|
||||
[2]: https://www.javascript.com/
|
||||
[3]: https://babeljs.io/
|
||||
[4]: https://gruntjs.com/
|
||||
[5]: https://bower.io/
|
||||
[6]: https://gulpjs.com/
|
||||
[7]: http://browserify.org/
|
||||
[8]: https://webpack.js.org/
|
||||
[9]: https://github.com/yarnpkg/yarn
|
||||
[10]: https://parceljs.org/
|
||||
[11]: https://rollupjs.org/guide/en/
|
||||
[12]: https://qmo.io/blog/javascript-tooling-the-evolution-and-future-of-js-front-end-build-tools/
|
||||
[13]: https://blog.logrocket.com/tools-and-modern-workflow-for-front-end-developers-505c7227e917/
|
||||
[14]: https://medium.com/@trevorpoppen/modern-front-end-the-tools-and-build-process-explained-36641b5c1a53
|
||||
[15]: https://www.developerdrive.com/best-build-tools-frontend-development/
|
||||
[16]: https://www.sheddy.xyz/posts/javascript-build-tools-past-and-beyond
|
@ -0,0 +1,233 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (A beginner's guide to Kubernetes Jobs and CronJobs)
|
||||
[#]: via: (https://opensource.com/article/20/11/kubernetes-jobs-cronjobs)
|
||||
[#]: author: (Mike Calizo https://opensource.com/users/mcalizo)
|
||||
|
||||
A beginner's guide to Kubernetes Jobs and CronJobs
|
||||
======
|
||||
Use Jobs and CronJobs to control and manage Kubernetes pods and
|
||||
containers.
|
||||
![Ships at sea on the web][1]
|
||||
|
||||
[Kubernetes][2] is the default orchestration engine for containers. Its options for controlling and managing pods and containers include:
|
||||
|
||||
1. Deployments
|
||||
2. StatefulSets
|
||||
3. ReplicaSets
|
||||
|
||||
|
||||
|
||||
Each of these features has its own purpose, with the common function to ensure that pods run continuously. In failure scenarios, these controllers either restart or reschedule pods to ensure the services in the pods continue running.
|
||||
|
||||
As the [Kubernetes documentation explains][3], a Kubernetes Job creates one or more pods and ensures that a specified number of the pods terminates when the task (Job) completes.
|
||||
|
||||
Just like in a typical operating system, the ability to perform automated, scheduled jobs without user interaction is important in the Kubernetes world. But Kubernetes Jobs do more than just run automated jobs, and there are multiple ways to utilize them through:
|
||||
|
||||
1. Jobs
|
||||
2. CronJobs
|
||||
3. Work queues (this is beyond the scope of this article)
|
||||
|
||||
|
||||
|
||||
Sounds simple right? Well, maybe. Anyone who works on containers and microservice applications knows that some require services to be transient so that they can do specific tasks for applications or within the Kubernetes clusters.
|
||||
|
||||
In this article, I will go into why Kubernetes Jobs are important, how to create Jobs and CronJobs, and when to use them for applications running on the Kubernetes cluster.
|
||||
|
||||
### Differences between Kubernetes Jobs and CronJobs
|
||||
|
||||
Kubernetes Jobs are used to create transient pods that perform specific tasks they are assigned to. [CronJobs][4] do the same thing, but they run tasks based on a defined schedule.
|
||||
|
||||
Jobs play an important role in Kubernetes, especially for running batch processes or important ad-hoc operations. Jobs differ from other Kubernetes controllers in that they run tasks until completion, rather than managing the desired state such as in Deployments, ReplicaSets, and StatefulSets.
|
||||
|
||||
### How to create Kubernetes Jobs and CronJobs
|
||||
|
||||
With that background in hand, you can start creating Jobs and CronJobs.
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
To do this exercise, you need to have the following:
|
||||
|
||||
1. A working Kubernetes cluster; you can install it with either:
|
||||
* [CentOS 8][5]
|
||||
* [Minikube][6]
|
||||
2. The [kubectl][7] Kubernetes command line
|
||||
|
||||
|
||||
|
||||
Here is the Minikube deployment I used for this demonstration:
|
||||
|
||||
|
||||
```
|
||||
$ minikube version
|
||||
minikube version: v1.8.1
|
||||
|
||||
$ kubectl cluster-info
|
||||
Kubernetes master is running at <https://172.17.0.59:8443>
|
||||
KubeDNS is running at <https://172.17.0.59:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy>
|
||||
|
||||
$ kubectl get nodes
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
minikube Ready master 88s v1.17.3
|
||||
```
|
||||
|
||||
#### Kubernetes Jobs
|
||||
|
||||
Just like anything else in the Kubernetes world, you can create Kubernetes Jobs with a definition file. Create a file called `sample-jobs.yaml` using your favorite editor.
|
||||
|
||||
Here is a snippet of the file that you can use to create an example Kubernetes Job:
|
||||
|
||||
|
||||
```
|
||||
apiVersion: batch/v1 ## The version of the Kubernetes API
|
||||
kind: Job ## The type of object for jobs
|
||||
metadata:
|
||||
name: job-test
|
||||
spec: ## What state you desire for the object
|
||||
template:
|
||||
metadata:
|
||||
name: job-test
|
||||
spec:
|
||||
containers:
|
||||
- name: job
|
||||
image: busybox ## Image used
|
||||
command: ["echo", "job-test"] ## Command used to create logs for verification later
|
||||
restartPolicy: OnFailure ## Restart Policy in case container failed
|
||||
```
|
||||
|
||||
Next, apply the Jobs in the cluster:
|
||||
|
||||
|
||||
```
|
||||
`$ kubectl apply -f sample-jobs.yaml`
|
||||
```
|
||||
|
||||
Wait a few minutes for the pods to be created. You can view the pod creation's status:
|
||||
|
||||
|
||||
```
|
||||
`$ kubectl get pod –watch`
|
||||
```
|
||||
|
||||
After a few seconds, you should see your pod created successfully:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl get pods
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
job-test 0/1 Completed 0 11s
|
||||
```
|
||||
|
||||
Once the pods are created, verify the Job's logs:
|
||||
|
||||
|
||||
```
|
||||
`$ kubectl logs job-test job-test`
|
||||
```
|
||||
|
||||
You have created your first Kubernetes Job, and you can explore details about it:
|
||||
|
||||
|
||||
```
|
||||
`$ kubectl describe job job-test`
|
||||
```
|
||||
|
||||
Clean up the Jobs:
|
||||
|
||||
|
||||
```
|
||||
`$ kubectl delete jobs job-test`
|
||||
```
|
||||
|
||||
#### Kubernetes CronJobs
|
||||
|
||||
You can use CronJobs for cluster tasks that need to be executed on a predefined schedule. As the [documentation explains][8], they are useful for periodic and recurring tasks, like running backups, sending emails, or scheduling individual tasks for a specific time, such as when your cluster is likely to be idle.
|
||||
|
||||
As with Jobs, you can create CronJobs via a definition file. Following is a snippet of the CronJob file `cron-test.yaml`. Use this file to create an example CronJob:
|
||||
|
||||
|
||||
```
|
||||
apiVersion: batch/v1beta1 ## The version of the Kubernetes API
|
||||
kind: CronJob ## The type of object for Cron jobs
|
||||
metadata:
|
||||
name: cron-test
|
||||
spec:
|
||||
schedule: "*/1 * * * *" ## Defined schedule using the *nix style cron syntax
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: cron-test
|
||||
image: busybox ## Image used
|
||||
args:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- date; echo Hello this is Cron test
|
||||
restartPolicy: OnFailure ## Restart Policy in case container failed
|
||||
```
|
||||
|
||||
Apply the CronJob to your cluster:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl apply -f cron-test.yaml
|
||||
cronjob.batch/cron-test created
|
||||
```
|
||||
|
||||
Verify that the CronJob was created with the schedule in the definition file:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl get cronjob cron-test
|
||||
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
|
||||
cron-test */1 * * * * False 0 <none> 10s
|
||||
```
|
||||
|
||||
After a few seconds, you can find the pods that the last scheduled job created and view the standard output of one of the pods:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl logs cron-test-1604870760
|
||||
Sun Nov 8 21:26:09 UTC 2020
|
||||
Hello from the Kubernetes cluster
|
||||
```
|
||||
|
||||
You have created a Kubernetes CronJob that creates an object once per execution based on the schedule `schedule: "*/1 * * * *"`. Sometimes the creation can be missed because of environmental issues in the cluster. Therefore, they need to be [idempotent][9].
|
||||
|
||||
### Other things to know
|
||||
|
||||
Unlike deployments and services in Kubernetes, you can't change the same Job configuration file and reapply it at once. When you make changes in the Job configuration file, you must delete the previous Job from the cluster before you apply it.
|
||||
|
||||
Generally, creating a Job creates a single pod and performs the given task, as in the example above. But by using completions and [parallelism][10], you can initiate several pods, one after the other.
|
||||
|
||||
### Use your Jobs
|
||||
|
||||
You can use Kubernetes Jobs and CronJobs to manage your containerized applications. Jobs are important in Kubernetes application deployment patterns where you need a communication mechanism along with interactions between pods and the platforms. This may include cases where an application needs a "controller" or a "watcher" to complete tasks or needs to be scheduled to run periodically.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/11/kubernetes-jobs-cronjobs
|
||||
|
||||
作者:[Mike Calizo][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/mcalizo
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/kubernetes_containers_ship_lead.png?itok=9EUnSwci (Ships at sea on the web)
|
||||
[2]: https://kubernetes.io/
|
||||
[3]: https://kubernetes.io/docs/concepts/workloads/controllers/job/
|
||||
[4]: https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/
|
||||
[5]: https://phoenixnap.com/kb/how-to-install-kubernetes-on-centos
|
||||
[6]: https://minikube.sigs.k8s.io/docs/start/
|
||||
[7]: https://kubernetes.io/docs/reference/kubectl/kubectl/
|
||||
[8]: https://v1-18.docs.kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/
|
||||
[9]: https://en.wikipedia.org/wiki/Idempotence
|
||||
[10]: https://kubernetes.io/docs/concepts/workloads/controllers/job/#parallel-jobs
|
105
sources/tech/20201123 Day 11- learning about learning rates.md
Normal file
105
sources/tech/20201123 Day 11- learning about learning rates.md
Normal file
@ -0,0 +1,105 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Day 11: learning about learning rates)
|
||||
[#]: via: (https://jvns.ca/blog/2020/11/23/day-11--learning-about-learning-rates/)
|
||||
[#]: author: (Julia Evans https://jvns.ca/)
|
||||
|
||||
Day 11: learning about learning rates
|
||||
======
|
||||
|
||||
Hello!
|
||||
|
||||
On Friday I trained an RNN to count to 3 (1 2 3 1 2 3 1 2 3), thanks to some great advice from another Recurser. I figured that once I got that working, I could then extend that and train the same RNN on a bunch of Shakespearean text to get it to generate vaguely Shakespeare-y text.
|
||||
|
||||
But on Friday I couldn’t get that to work! I was puzzled by this, but today I figured out what was happening.
|
||||
|
||||
### what’s a learning rate?
|
||||
|
||||
First, here’s a very short “deep learning for math majors” explanation of how training for a deep learning model works in general. I wrote this to help consolidate my own understanding on Friday.
|
||||
|
||||
* The model takes the training data and weights and outputs a single number, the output of the loss function
|
||||
* The model’s “weights” are the parameters of all the matrices in the model, like if there’s a 64 x 64 matrix then there are 4096 weights
|
||||
* For optimization purposes, this function should be thought of as a function of the weights (not the training data), since the weights are going to change and the function isn’t
|
||||
* Training is basically gradient descent. You take the derivative of the function (aka gradient), with respect to the weights of all the matrices/various functions in the model
|
||||
* The way you take this derivative is using the chain rule, the algorithm for applying the chain rule to a neural network is called “backpropagation”
|
||||
* Then you adjust the parameters by a multiple of the gradient (since this is gradient descent). The multiple of the gradient that you use is called the **learning rate** – it’s basically `parameters -= learning_rate * gradient`
|
||||
* machine learning model training is a lot like general continuous function optimization in that finding the “right” step size to do gradient descent is basically impossible so there are a lot of heuristics for picking step learning rates that will work. One of these heuristics is called [Adam][1]
|
||||
|
||||
|
||||
|
||||
### if you set your learning rate too high, the model won’t learn anything
|
||||
|
||||
So back to our original problem: when I was training my model to generate Shakespeare, I noticed that my model wasn’t learning anything! By “not learning anything”, I mean that the value of the loss function was not going down over time.
|
||||
|
||||
I eventually figured out that this was because my learning rate was too high! It was 0.01 or something, and changing it to more like 0.002 resulted in more learning progress. Hooray!
|
||||
|
||||
I started to generate text like this:
|
||||
|
||||
```
|
||||
erlon, w oller. is. d y ivell iver ave esiheres tligh? e ispeafeink
|
||||
teldenauke'envexes. h exinkes ror h. ser. sat ly. spon, exang oighis yn, y
|
||||
hire aning is's es itrt. for ineull ul'cl r er. s unt. y ch er e s out twiof
|
||||
uranter h measaker h exaw; speclare y towessithisil's aches? s es, tith s aat
|
||||
```
|
||||
|
||||
which is a big improvement over what I had previously, which was:
|
||||
|
||||
```
|
||||
kf ;o 'gen '9k ',nrhna 'v ;3; ;'rph 'g ;o kpr ;3;tavrnad 'ps ;]; ;];oraropr
|
||||
;9vnotararaelpot ;9vr ;9
|
||||
```
|
||||
|
||||
But then training stalled again, and I felt like I could still do better.
|
||||
|
||||
### resetting the state of the optimizer is VERY BAD
|
||||
|
||||
It turned out that the reason training had stalled the second time was that my code looked like this:
|
||||
|
||||
```
|
||||
for i in range(something):
|
||||
optimizer = torch.optim.Adam(rnn.parameters())
|
||||
... do training things
|
||||
```
|
||||
|
||||
I’d written the code this way because I didn’t realize that the state of the optimizer (“Adam”) was important, so I just reset it sometimes because it seemed convenient at the time.
|
||||
|
||||
It turns out that the optimizer’s state is very important, I think because it slowly reduces the training rate as training progresses. So I reorganized my code so that I only initialized the optimizer once at the beginning of training.
|
||||
|
||||
I also made sure that when I saved my model, I also saved the optimizer’s state:
|
||||
|
||||
```
|
||||
torch.save({'model_state_dict': rnn.state_dict(), 'optimizer_dict': optimizer.state_dict()}, MODEL_PATH)
|
||||
```
|
||||
|
||||
Here’s the “Shakespeare” the model was generating after I stopped resetting the optimizer all the time:
|
||||
|
||||
```
|
||||
at soerin, I kanth as jow gill fimes, To metes think our wink we in fatching
|
||||
and, Drose, How the wit? our arpear War, our in wioken alous, To thigh dies wit
|
||||
stain! navinge a sput pie, thick done a my wiscian. Hark's king, and Evit night
|
||||
and find. Woman steed and oppet, I diplifire, and evole witk ud
|
||||
```
|
||||
|
||||
It’s a big improvement! There are some actual English words in there! “Woman steed and oppet!”
|
||||
|
||||
### that’s it for today!
|
||||
|
||||
Tomorrow my goal is to learn what “BPTT” means and see if I can use it to train this model more quickly and maybe give it a bigger hidden state than 87 parameters. And once I’ve done that, maybe I can start to train more interesting models!!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://jvns.ca/blog/2020/11/23/day-11--learning-about-learning-rates/
|
||||
|
||||
作者:[Julia Evans][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://jvns.ca/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Adam
|
@ -0,0 +1,284 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (A beginner's guide to developing with React)
|
||||
[#]: via: (https://opensource.com/article/20/11/reactjs-tutorial)
|
||||
[#]: author: (Shedrack Akintayo https://opensource.com/users/shedrack-akintayo)
|
||||
|
||||
A beginner's guide to developing with React
|
||||
======
|
||||
A step-by-step guide to using React in your web and mobile user
|
||||
interfaces.
|
||||
![Gears connecting][1]
|
||||
|
||||
[React][2] is a JavaScript user interface (UI) library that was built and is maintained by Facebook. React helps JavaScript developers think logically and functionally about how they want to build a UI.
|
||||
|
||||
With React, you can build:
|
||||
|
||||
1. Single-page applications
|
||||
2. Applications that are easy to understand
|
||||
3. Scalable applications
|
||||
4. Cross-platform applications
|
||||
|
||||
|
||||
|
||||
React allows developers to build applications declaratively and offers a unidirectional flow of data.
|
||||
|
||||
### React's advantages
|
||||
|
||||
The following features explain why React is one of the [most popular][3] web frameworks.
|
||||
|
||||
* **It is declarative:** React makes it extremely painless to build interactive user interfaces, design basic views for your application based on various states, and update and render new views when the data in your application changes.
|
||||
* **It is component-based:** React gives you the ability to build encapsulated components that can manage their own state, then puts them together to build complex UIs. The logic of these components is written in JavaScript instead of templates, so you easily pass actual data and keep state out of the [document object model][4] (DOM).
|
||||
* **You can learn once, write anywhere:** React gives you the ability to build for both mobile (React Native) and the web. There's no need to rewrite your existing codebase; you can just integrate React with your existing code.
|
||||
* **The virtual DOM:** React introduced a wrapper around the regular DOM called the virtual DOM (VDOM). This allows React to render elements and update its state faster than the regular DOM.
|
||||
* **Performance:** React has great performance benefits due to the VDOM and one-way flow of data.
|
||||
|
||||
|
||||
|
||||
### The virtual DOM
|
||||
|
||||
React's VDOM is like a virtual copy of the original DOM. It offers one-way data binding, which makes manipulating and updating the VDOM quicker than updating the original DOM. The VDOM can handle multiple operations in milliseconds without affecting the general page performance.
|
||||
|
||||
This VDOM supports React's declarative API: You basically tell React what state you want the UI to be in, and it ensures that the DOM matches that state.
|
||||
|
||||
### Prerequisites for learning React
|
||||
|
||||
Learning React requires basic knowledge of JavaScript, HTML, and CSS. To use React's power effectively, it helps to be familiar with [ECMAScript 6][5] (ES6) and functional and object-oriented programming.
|
||||
|
||||
You also need the following things installed on your computer:
|
||||
|
||||
* [NodeJS][6]
|
||||
* [npm][7] (comes bundled with NodeJS)
|
||||
* [Yarn][8] (an alternative to NPM)
|
||||
|
||||
|
||||
|
||||
### Basic React concepts
|
||||
|
||||
It also helps to have an understanding of React's concepts.
|
||||
|
||||
#### Components
|
||||
|
||||
Components are standalone, reusable pieces of code. They have the same purpose as JavaScript functions but work alone and return HTML via a built-in render function. They are two main types of components:
|
||||
|
||||
* **Class components** offer more control in the form of lifecycle hooks, managing and handling state, and API calls. For example: [code] class MyComponent extends React.Component {
|
||||
render() {
|
||||
return <div>This is a class component</div>;
|
||||
}
|
||||
}
|
||||
```
|
||||
* **Functional components** were used for rendering just views without any form of state management or data request until [React Hooks][9] was introduced. For example: [code] Function myComponent() {
|
||||
return (
|
||||
<div>A functional Component</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### Props
|
||||
|
||||
React props are like function arguments in JavaScript and attributes in HTML. They are read-only. For example:
|
||||
|
||||
|
||||
```
|
||||
function Welcome(props) {
|
||||
return <h1>Hello, {props.name}</h1>;
|
||||
}
|
||||
```
|
||||
|
||||
#### State
|
||||
|
||||
React components have a built-in object called _state_, which is where you store property values that belong to a particular component. If a component's state changes at any point in time, the component re-renders. For example:
|
||||
|
||||
|
||||
```
|
||||
class Car extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { brand: 'Ford' };
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>My Car</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### JSX
|
||||
|
||||
JSX is a syntax extension to JavaScript. It is similar to a template language but has the full power of JavaScript. JSX is compiled to `React.createElement()` calls, which return plain JavaScript objects called _React elements_. For example:
|
||||
|
||||
|
||||
```
|
||||
return (
|
||||
<div>
|
||||
<h1>My Car</h1>
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
The code between the return method that looks like HTML is JSX.
|
||||
|
||||
### How to use React
|
||||
|
||||
Ready to get started? I'll go step-by-step through two options for using React in your app:
|
||||
|
||||
* Adding its content delivery network (CDN) to your HTML file
|
||||
* Starting a blank React app with Create React App
|
||||
|
||||
|
||||
|
||||
#### Add its CDN to your HTML file
|
||||
|
||||
You can quickly use React in your HTML page by adding its CDN directly to your HTML file using the following steps:
|
||||
|
||||
**Step 1:** In the HTML page you want to add React to, add an empty `<div>` tag to create a container where you want to render something with React. For example:
|
||||
|
||||
|
||||
```
|
||||
<!-- ... old HTML ... -->
|
||||
|
||||
<[div][10] id="button_container"></[div][10]>
|
||||
|
||||
<!-- ... old HTML ... -->
|
||||
```
|
||||
|
||||
**Step 2:** Add three `<script>` tags to the HTML page just before the closing `</body>` tag. For example:
|
||||
|
||||
|
||||
```
|
||||
<!-- ... Some other HTML ... -->
|
||||
<!-- Initiate React. -->
|
||||
<!-- Note: when deploying, replace "development.js" with "production.min.js". -->
|
||||
|
||||
<[script][11] src="<https://unpkg.com/react@16/umd/react.development.js>" crossorigin></[script][11]>
|
||||
<[script][11] src="<https://unpkg.com/react-dom@16/umd/react-dom.development.js>" crossorigin></[script][11]>
|
||||
|
||||
<!-- Load our React component. -->
|
||||
|
||||
<[script][11] src="button.js"></[script][11]>
|
||||
|
||||
</[body][12]>
|
||||
```
|
||||
|
||||
The first two script tags load React, and the last one loads your React component code.
|
||||
|
||||
**Step 3:** Create a file called `button.js` in the same folder as your HTML page to hold the code for your React component. Paste the following code inside your `button.js` file:
|
||||
|
||||
|
||||
```
|
||||
'use strict';
|
||||
const e = React.createElement;
|
||||
class Button extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { clicked: false };
|
||||
}
|
||||
render() {
|
||||
if (this.state.clicked) {
|
||||
return 'You clicked this button.';
|
||||
}
|
||||
return e(
|
||||
'button',
|
||||
{ onClick: () => this.setState({ clicked: true }) },
|
||||
'Click Me'
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This code creates a button component that returns a message when the button is clicked.
|
||||
|
||||
**Step 4:** To use this component in your HTML page, add the code snippet below at the end of your `button.js` file:
|
||||
|
||||
|
||||
```
|
||||
const domContainer = document.querySelector('#button_container');
|
||||
ReactDOM.render(e(Button), domContainer);
|
||||
```
|
||||
|
||||
The code snippet above targets the `<div>` you added to your HTML in the first step and render your React button component inside it.
|
||||
|
||||
#### Start a blank React app with Create React App
|
||||
|
||||
If you want to start with a blank React app, use [Create React App][13]. This is the recommended way to quickly create single-page React applications, as it provides a modern build setup without any configuration.
|
||||
|
||||
To generate a new React app using Create React App, enter one of the following commands in your terminal. This will create a new React app called `my-app:`
|
||||
|
||||
* [npx][14] (this is the recommended way to use create-react-app): [code]`npx create-react-app my-app`
|
||||
```
|
||||
* npm: [code]`npm i -g create-react-app && npm create-react-app my-app`
|
||||
```
|
||||
* Yarn: [code]`yarn add create-react-app && yarn create-react-app my-app`[/code] ` `
|
||||
|
||||
|
||||
|
||||
To run your newly created app, navigate into the app folder (by typing `cd my-app` into your terminal) and enter one of the following commands:
|
||||
|
||||
* npm:
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
* Yarn:
|
||||
```
|
||||
yarn start
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
These will run the app you just created in development mode. You can open `http://localhost:3000` to view it in the browser.
|
||||
|
||||
When you navigate to `http://localhost:3000`, you should see a page like the one below. Any change you make in the React code will automatically render here.
|
||||
|
||||
![React gif][15]
|
||||
|
||||
I hope this guide to getting started with React has been helpful. There are many more things to discover in the world of JavaScript, so please explore on your own and share what you learn.
|
||||
|
||||
* * *
|
||||
|
||||
_This article originally appeared on [Shedrack Akintayo's blog][16] and is republished with his permission._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/11/reactjs-tutorial
|
||||
|
||||
作者:[Shedrack Akintayo][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/shedrack-akintayo
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/gears_wheels.png?itok=KRvpBttl (Gears connecting)
|
||||
[2]: https://reactjs.org/
|
||||
[3]: https://insights.stackoverflow.com/survey/2020#technology-web-frameworks
|
||||
[4]: https://en.wikipedia.org/wiki/Document_Object_Model
|
||||
[5]: https://en.wikipedia.org/wiki/ECMAScript
|
||||
[6]: https://nodejs.org/
|
||||
[7]: https://www.npmjs.com/
|
||||
[8]: https://yarnpkg.com/
|
||||
[9]: https://reactjs.org/docs/hooks-intro.html
|
||||
[10]: http://december.com/html/4/element/div.html
|
||||
[11]: http://december.com/html/4/element/script.html
|
||||
[12]: http://december.com/html/4/element/body.html
|
||||
[13]: https://create-react-app.dev/docs/getting-started/
|
||||
[14]: https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b
|
||||
[15]: https://opensource.com/sites/default/files/uploads/react.gif (React gif)
|
||||
[16]: https://www.sheddy.xyz/posts/react-js-the-first-time
|
@ -0,0 +1,295 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Build a motion detection system with a Raspberry Pi)
|
||||
[#]: via: (https://opensource.com/article/20/11/motion-detection-raspberry-pi)
|
||||
[#]: author: (Lukas Janėnas https://opensource.com/users/lukasjan)
|
||||
|
||||
Build a motion detection system with a Raspberry Pi
|
||||
======
|
||||
Set up an inexpensive home security system to alert you when someone is
|
||||
lurking around your house.
|
||||
![Houses in a row][1]
|
||||
|
||||
If you want a home security system to tell you if someone is lurking around your property, you don't need an expensive, proprietary solution from a third-party vendor. You can set up your own system using a Raspberry Pi, a passive infrared (PIR) motion sensor, and an LTE modem that will send SMS messages whenever it detects movement.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
You will need:
|
||||
|
||||
* A Raspberry Pi with Ethernet connection and Raspberry Pi OS
|
||||
* An HC-SR501 PIR motion sensor
|
||||
* 1 red LED
|
||||
* 1 green LED
|
||||
* An LTE modem (I used the Teltonika [TRM240][2])
|
||||
* A SIM card
|
||||
|
||||
|
||||
|
||||
#### The PIR motion sensor
|
||||
|
||||
The PIR motion sensor will sense motion when something that emits infrared rays (e.g., a human, animal, or anything that emits heat) moves in the range of the sensor's field or reach. PIR motion sensors are low power and inexpensive, so they're used in many products that detect motion. They can't say how many people are in the area and how close they are to the sensor; they just detect motion.
|
||||
|
||||
![PIR sensor][3]
|
||||
|
||||
(Lukas Janenas, [CC BY-SA 4.0][4])
|
||||
|
||||
A PIR sensor has two potentiometers, one for setting the delay time and another for sensitivity. The delay time adjustment sets how long the output should remain high after detecting motion; it can be anywhere from five seconds to five minutes. The sensitivity adjustment sets the detection range, which can be anywhere from three to seven meters.
|
||||
|
||||
#### The LTE modem
|
||||
|
||||
Long-term evolution (LTE) is a standard for wireless broadband communication based on the GSM/EDGE and UMTS/HSPA technologies. The LTE modem I'm using is a USB device that can add 3G or 4G (LTE) cellular connectivity to a Raspberry PI computer. In this project, I'm not using the modem for cellular connectivity, but to send messages to my phone when motion is detected. I can control the modem with serial communication and AT commands; the latter send messages from the modem to my phone number.
|
||||
|
||||
![LTE modem][5]
|
||||
|
||||
(Lukas Janenas, [CC BY-SA 4.0][4])
|
||||
|
||||
### How to set up your home security system
|
||||
|
||||
#### Step 1: Install the software
|
||||
|
||||
First, install the necessary software on your Raspberry Pi. In the Raspberry Pi's terminal, enter:
|
||||
|
||||
|
||||
```
|
||||
`sudo apt install python3 python3-gpiozero python-serial -y`
|
||||
```
|
||||
|
||||
#### Step 2: Set up the modem
|
||||
|
||||
Insert your SIM card into your LTE modem by following these [instructions][6] for the TRM240. Make sure to mount the antenna on the modem for a better signal.
|
||||
|
||||
#### Step 3: Connect the modem to the Raspberry Pi
|
||||
|
||||
Connect the LTE modem to one of the Raspberry Pi's USB ports, then wait for the device to boot up. You should see four new USB ports in the `/dev` directory. You can check them by executing this command in the terminal:
|
||||
|
||||
|
||||
```
|
||||
`ls /dev/ttyUSB*`
|
||||
```
|
||||
|
||||
You should now see these devices:
|
||||
|
||||
![USB ports output][7]
|
||||
|
||||
(Lukas Janenas, [CC BY-SA 4.0][4])
|
||||
|
||||
You will use the **ttyUSB2** port to communicate with the device by sending AT commands.
|
||||
|
||||
#### Step 4: Connect the sensors to the Raspberry Pi
|
||||
|
||||
1. **Connect the PIR sensor:**
|
||||
Connect the **VCC** and **GND** pins to the respective pins on the Raspberry Pi, and connect the motion sensor's output pin to the **8 pin** on the Raspberry Pi. See below for a schematic on these connections, and you can learn more about [Raspberry Pi pin numbering][8] in the GPIO Zero docs.
|
||||
|
||||
2. **Connect the LED lights:**
|
||||
If you want indicator LEDs to light up when motion is detected, connect the cathode (short leg, flat side) of the LED to a ground pin; connect the anode (longer leg) to a current-limiting resistor; and connect the other side of the resistor to a GPIO pin (the limiting resistor can be placed either side of the LED). Connect the red LED to the **38 pin** on the board and the green LED to the **40 pin**.
|
||||
|
||||
|
||||
|
||||
|
||||
Note that this step is optional. If you don't want indicator LEDs when motion is detected, delete the LED sections from the example code below.
|
||||
|
||||
![Raspberry Pi wiring diagram][9]
|
||||
|
||||
(Lukas Janenas, [CC BY-SA 4.0][4])
|
||||
|
||||
#### Step 5: Launch the program
|
||||
|
||||
Using the terminal (or any text editor), create a file named `motion_sensor.py`, and paste in the [example code][10] below.
|
||||
|
||||
Find and change these fields:
|
||||
|
||||
* `phone_number`
|
||||
* `message`
|
||||
|
||||
|
||||
|
||||
If you used different pins to connect the sensors, make sure to change the code accordingly.
|
||||
|
||||
When everything is set and ready to go, start the program from the terminal by entering:
|
||||
|
||||
|
||||
```
|
||||
`python3 motion_sensor.py`
|
||||
```
|
||||
|
||||
If you don't have the required privileges to start the program, try this command:
|
||||
|
||||
|
||||
```
|
||||
`sudo python3 motion_sensor.py`
|
||||
```
|
||||
|
||||
![Motion sensor hardware][11]
|
||||
|
||||
(Lukas Janenas, [CC BY-SA 4.0][4])
|
||||
|
||||
### Example code
|
||||
|
||||
|
||||
```
|
||||
from gpiozero import MotionSensor, LED
|
||||
from time import sleep, time
|
||||
from sys import exit
|
||||
import serial
|
||||
import threading
|
||||
|
||||
# Raspberry Pi GPIO pin config
|
||||
sensor = MotionSensor(14)
|
||||
green = LED(21)
|
||||
red = LED(20)
|
||||
|
||||
# Modem configuration
|
||||
device = '/dev/ttyUSB2'
|
||||
message = '<message>'
|
||||
phone_number = '<phone_number>'
|
||||
sms_timeout = 120 # min seconds between SMS messages
|
||||
|
||||
def setup():
|
||||
port.close()
|
||||
|
||||
try:
|
||||
port.open()
|
||||
except serial.SerialException as e:
|
||||
print('Error opening device: ' + str(e))
|
||||
return False
|
||||
|
||||
# Turn off echo mode
|
||||
port.write(b'ATE0 \r')
|
||||
if not check_response('OK', 10):
|
||||
print('Failed on ATE0')
|
||||
return False
|
||||
|
||||
# Enter SMS text mode
|
||||
port.write(b'AT+CMGF=1 \r')
|
||||
if not check_response('OK', 6):
|
||||
print('Failed on CMGF')
|
||||
return False
|
||||
|
||||
# Switch character set to 'international reference alphabet'
|
||||
# Note: this still doesn't support all characters
|
||||
port.write(b'AT+CSCS="IRA" \r')
|
||||
if not check_response('OK', 6):
|
||||
print('Failed on CSCS')
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def check_response(string, amount):
|
||||
result = ''
|
||||
|
||||
try:
|
||||
result = port.read(amount).decode()
|
||||
except:
|
||||
return False
|
||||
|
||||
if not string in result:
|
||||
try:
|
||||
# Write 'ESC' to exit SMS input mode, just in case
|
||||
port.write(b'\x1B \r')
|
||||
except:
|
||||
return False
|
||||
|
||||
return string in result
|
||||
|
||||
def send_sms():
|
||||
global currently_sending, last_msg_time
|
||||
currently_sending = True
|
||||
|
||||
try:
|
||||
port.write('AT+CMGS="{}" \r'.format(phone_number).encode())
|
||||
if not check_response('>', 6):
|
||||
print('Failed on CMGS')
|
||||
currently_sending = False
|
||||
return
|
||||
|
||||
# Write the message terminated by 'Ctrl+Z' or '1A' in ASCII
|
||||
port.write('{}\x1A \r'.format(message).encode())
|
||||
|
||||
while True:
|
||||
result = port.readline().decode()
|
||||
|
||||
if 'OK' in result:
|
||||
print('> SMS sent successfully')
|
||||
last_msg_time = time()
|
||||
currently_sending = False
|
||||
return
|
||||
|
||||
if 'ERROR' in result:
|
||||
print('> Failed to send SMS [{}]'.format(result.rstrip()))
|
||||
currently_sending = False
|
||||
return
|
||||
except:
|
||||
# Initiate setup if the got while the program was running
|
||||
setup()
|
||||
currently_sending = False
|
||||
|
||||
def on_motion():
|
||||
print('Motion detected!')
|
||||
green.off()
|
||||
red.on()
|
||||
|
||||
if time() - last_msg_time > sms_timeout and not currently_sending:
|
||||
print('> Sending SMS...')
|
||||
threading.Thread(target=send_sms).start()
|
||||
|
||||
def no_motion():
|
||||
green.on()
|
||||
red.off()
|
||||
|
||||
print('* Setting up...')
|
||||
green.on()
|
||||
red.on()
|
||||
|
||||
port = serial.Serial()
|
||||
port.port = device
|
||||
port.baudrate = 115200
|
||||
port.timeout = 2
|
||||
|
||||
last_msg_time = 0
|
||||
currently_sending = False
|
||||
|
||||
if not setup():
|
||||
print('* Retrying...')
|
||||
if not setup():
|
||||
print('* Try restarting the modem')
|
||||
exit(1)
|
||||
|
||||
print('* Do not move, setting up the PIR sensor...')
|
||||
sensor.wait_for_no_motion()
|
||||
|
||||
print('* Device ready! ', end='', flush=True)
|
||||
green.on()
|
||||
red.off()
|
||||
|
||||
sensor.when_motion = on_motion
|
||||
sensor.when_no_motion = no_motion
|
||||
input('Press Enter or Ctrl+C to exit\n\n')
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/11/motion-detection-raspberry-pi
|
||||
|
||||
作者:[Lukas Janėnas][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/lukasjan
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/house_home_colors_live_building.jpg?itok=HLpsIfIL (Houses in a row)
|
||||
[2]: https://teltonika-networks.com/product/trm240/
|
||||
[3]: https://opensource.com/sites/default/files/uploads/pir-sensor.jpg (PIR sensor)
|
||||
[4]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[5]: https://opensource.com/sites/default/files/uploads/modem.jpg (LTE modem)
|
||||
[6]: https://wiki.teltonika-networks.com/view/TRM240_SIM_Card
|
||||
[7]: https://opensource.com/sites/default/files/uploads/deviceoutput.png (USB ports output)
|
||||
[8]: https://gpiozero.readthedocs.io/en/stable/recipes.html#pin-numbering
|
||||
[9]: https://opensource.com/sites/default/files/uploads/wiring.png (Raspberry Pi wiring diagram)
|
||||
[10]: tmp.4ePb9Wjuou#code
|
||||
[11]: https://opensource.com/sites/default/files/uploads/motionssensorsetup.jpg (Motion sensor hardware)
|
@ -0,0 +1,113 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Compare Files and Folders Graphically in Linux With Meld)
|
||||
[#]: via: (https://itsfoss.com/meld-gui-diff/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
Compare Files and Folders Graphically in Linux With Meld
|
||||
======
|
||||
|
||||
How do you compare two similar files to check for differences? The obvious answer is [to use the diff command in Linux][1].
|
||||
|
||||
The problem is that not everyone would be comfortable comparing files in Linux terminal. And the diff command output could be confusing for some.
|
||||
|
||||
Take this diff command output for example:
|
||||
|
||||
![][2]
|
||||
|
||||
There is definitely a learning curve involved here. However, if you are using desktop Linux, you can use [GUI][3] applications to easily compare two files for any differences.
|
||||
|
||||
There are several GUI diff tools available for Linux. I am going to highlight my favorite tool Meld in this week’s Linux application highlight.
|
||||
|
||||
### Meld: Visual Diff and Merge tool for Linux (and Windows)
|
||||
|
||||
With [Meld][4], you can compare two files in side by side view. Not only that, you may also modify the files to make changes accordingly. That’s what you would want to do in most situations, right?
|
||||
|
||||
![File Comparison][5]
|
||||
|
||||
Meld is also capable of comparing directories and show which files are different. It will also show while files are new or missing.
|
||||
|
||||
![Directory Comparison][6]
|
||||
|
||||
You may also use Meld for a three-way comparison.
|
||||
|
||||
![Three Way File Comparison][7]
|
||||
|
||||
The graphical side-by-side comparison helps in a number of situations. If you are a developer, you can use it to understand code patches. Meld also supports version control systems like Git, [Mercurial][8], [Subversion][9] etc.
|
||||
|
||||
### Features of Meld
|
||||
|
||||
![][10]
|
||||
|
||||
The open source Meld tools has the following main features:
|
||||
|
||||
* Perform two and three-way difference comparison
|
||||
* Edit files in-place and the difference comparison updates immediately
|
||||
* Navigate between differences and conflicts
|
||||
* Visualize global and local differences with insertions, changes and conflicts marked accordingly
|
||||
* Use regex text filtering to ignore certain differences
|
||||
* Syntax highlighting
|
||||
* Compare two or three directories for newly added, missing and altered files
|
||||
* Exclude some files from comparison
|
||||
* Support for popular version control systems like Git, Mercurial, Bazaar and SVN
|
||||
* Support for many international languages
|
||||
* Open source GPL v2 license
|
||||
* Available for Linux as well as Windows
|
||||
|
||||
|
||||
|
||||
### Installing Meld on Linux
|
||||
|
||||
Meld is a popular application and it is available in the official repositories of most Linux distributions.
|
||||
|
||||
Check your distribution’s software center and see if Meld is available.
|
||||
|
||||
![Meld In Ubuntu Software Center][11]
|
||||
|
||||
Alternatively, you can also use command line package manager of your distribution to install Meld. On [Ubuntu, it is available in the Universe repository][12] and can be [installed using the apt command][13]:
|
||||
|
||||
```
|
||||
sudo apt install meld
|
||||
```
|
||||
|
||||
You may find the source code of Meld on GNOME’s GitLab repository:
|
||||
|
||||
[Meld Source Code][14]
|
||||
|
||||
### Worth it?
|
||||
|
||||
I know that [most modern open source code editors][15] come with this feature but sometimes you just want a simple interface without the trouble of installing additional add-ons for comparing files. Meld provides you just that.
|
||||
|
||||
Do you use some other tools for checking differences between files? Which tool would that be? What’s your experience with Meld, if you ever used it? The comment sections is all yours for sharing your opinion.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/meld-gui-diff/
|
||||
|
||||
作者:[Abhishek Prakash][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/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://linuxhandbook.com/diff-command/
|
||||
[2]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/11/diff-command-complicated-output.png?resize=795%2C551&ssl=1
|
||||
[3]: https://itsfoss.com/gui-cli-tui/
|
||||
[4]: https://meldmerge.org
|
||||
[5]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/11/file-comaprison-in-Linux-with-meld.png?resize=800%2C498&ssl=1
|
||||
[6]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/11/directory-comparison-in-Linux-with_meld.png?resize=800%2C497&ssl=1
|
||||
[7]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/11/three-way-file-comaprison-with-meld-in-linux.png?resize=800%2C466&ssl=1
|
||||
[8]: https://www.mercurial-scm.org/
|
||||
[9]: https://subversion.apache.org/
|
||||
[10]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/11/meld-visual-diff.png?resize=786%2C455&ssl=1
|
||||
[11]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/11/Meld-in-Ubuntu-Software-Center.png?resize=800%2C384&ssl=1
|
||||
[12]: https://itsfoss.com/ubuntu-repositories/
|
||||
[13]: https://itsfoss.com/apt-command-guide/
|
||||
[14]: https://gitlab.gnome.org/GNOME/meld
|
||||
[15]: https://itsfoss.com/best-modern-open-source-code-editors-for-linux/
|
@ -0,0 +1,638 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Create a machine learning model with Bash)
|
||||
[#]: via: (https://opensource.com/article/20/11/machine-learning-bash)
|
||||
[#]: author: (Girish Managoli https://opensource.com/users/gammay)
|
||||
|
||||
Create a machine learning model with Bash
|
||||
======
|
||||
Bash, Tcsh, or Zsh can help you get ready for machine learning.
|
||||
![bash logo on green background][1]
|
||||
|
||||
[Machine learning][2] is a powerful computing capability for predicting or forecasting things that conventional algorithms find challenging. The machine learning journey begins with collecting and preparing data—a _lot_ of it—then it builds mathematical models based on that data. While multiple tools can be used for these tasks, I like to use the [shell][3].
|
||||
|
||||
A shell is an interface for performing operations using a defined language. This language can be invoked interactively or scripted. The concept of the shell was introduced in [Unix][4] operating systems in the 1970s. Some of the most popular shells include [Bash][5], [tcsh][6], and [Zsh][7]. They are available for all operating systems, including Linux, macOS, and Windows, which gives them high portability. For this exercise, I'll use Bash.
|
||||
|
||||
This article is an introduction to using a shell for data collection and data preparation. Whether you are a data scientist looking for efficient tools or a shell expert looking at using your skills for machine learning, I hope you will find valuable information here.
|
||||
|
||||
The example problem in this article is creating a machine learning model to forecast temperatures for US states. It uses shell commands and scripts to do the following data collection and data preparation steps:
|
||||
|
||||
1. Download data
|
||||
2. Extract the necessary fields
|
||||
3. Aggregate data
|
||||
4. Make time series
|
||||
5. Create the train, test, and validate data sets
|
||||
|
||||
|
||||
|
||||
You may be asking why you should do this with shell, when you can do all of it in a machine learning programming language such as [Python][8]. This is a good question. If data processing is performed with an easy, friendly, rich technology like a shell, a data scientist focuses only on machine learning modeling and not the details of a language.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
First, you need to have a shell interpreter installed. If you use Linux or macOS, it will already be installed, and you may already be familiar with it. If you use Windows, try [MinGW][9] or [Cygwin][10].
|
||||
|
||||
For more information, see:
|
||||
|
||||
* [Bash tutorials][11] here on opensource.com
|
||||
* The official [shell scripting tutorial][12] by Steve Parker, the creator of Bourne shell
|
||||
* The [Bash Guide for Beginners][13] by the Linux Documentation Project
|
||||
* If you need help with a specific command, type `<commandname> --help` in the shell for help; for example: `ls --help`.
|
||||
|
||||
|
||||
|
||||
## Get started
|
||||
|
||||
Now that your shell is set up, you can start preparing data for the machine learning temperature-prediction problem.
|
||||
|
||||
### 1\. Download data
|
||||
|
||||
The data for this tutorial comes from the US National Oceanic and Atmospheric Administration (NOAA). You will train your model using the last 10 complete years of data. The data source is at <https://www1.ncdc.noaa.gov/pub/data/ghcn/daily/by_year/>, and the data is in .csv format and gzipped.
|
||||
|
||||
Download and unzip the data using a [shell script][14]. Use your favorite text editor to create a file named `download.sh` and paste in the code below. The comments in the code explain what the commands do:
|
||||
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
# This is called hashbang. It identifies the executor used to run this file.
|
||||
# In this case, the script is executed by shell itself.
|
||||
# If not specified, a program to execute the script must be specified.
|
||||
# With hashbang: ./download.sh; Without hashbang: sh ./download.sh;
|
||||
|
||||
FROM_YEAR=2010
|
||||
TO_YEAR=2019
|
||||
|
||||
year=$FROM_YEAR
|
||||
# For all years one by one starting from FROM_YEAR=2010 upto TO_YEAR=2019
|
||||
while [ $year -le $TO_YEAR ]
|
||||
do
|
||||
# show the year being downloaded now
|
||||
echo $year
|
||||
# Download
|
||||
wget <https://www1.ncdc.noaa.gov/pub/data/ghcn/daily/by\_year/${year}.csv.gz>
|
||||
# Unzip
|
||||
gzip -d ${year}.csv.gz
|
||||
# Move to next year by incrementing
|
||||
year=$(($year+1))
|
||||
done
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
* If you are behind a proxy server, consult Mark Grennan's [how-to][15], and use: [code] export http_proxy=<http://username:password@proxyhost:port/>
|
||||
export https_proxy=<https://username:password@proxyhost:port/>
|
||||
```
|
||||
* Make sure all standard commands are already in your PATH (such as `/bin` or `/usr/bin`). If not, [set your PATH][16].
|
||||
* [Wget][17] is a utility for connecting to web servers from the command line. If Wget is not installed on your system, [download it][18].
|
||||
* Make sure you have [gzip][19], a utility used for compression and decompression.
|
||||
|
||||
|
||||
|
||||
Run this script to download, extract, and make 10 years' worth of data available as CSVs:
|
||||
```
|
||||
|
||||
|
||||
$ ./download.sh
|
||||
2010
|
||||
\--2020-10-30 19:10:47-- <https://www1.ncdc.noaa.gov/pub/data/ghcn/daily/by\_year/2010.csv.gz>
|
||||
Resolving www1.ncdc.noaa.gov (www1.ncdc.noaa.gov)... 205.167.25.171, 205.167.25.172, 205.167.25.178, ...
|
||||
Connecting to www1.ncdc.noaa.gov (www1.ncdc.noaa.gov)|205.167.25.171|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 170466817 (163M) [application/gzip]
|
||||
Saving to: '2010.csv.gz'
|
||||
|
||||
0K .......... .......... .......... .......... .......... 0% 69.4K 39m57s
|
||||
50K .......... .......... .......... .......... .......... 0% 202K 26m49s
|
||||
100K .......... .......... .......... .......... .......... 0% 1.08M 18m42s
|
||||
|
||||
...
|
||||
|
||||
```
|
||||
The [ls][20] command lists the contents of a folder. Use `ls 20*.csv` to list all your files with names beginning with 20 and ending with .csv.
|
||||
```
|
||||
|
||||
|
||||
$ ls 20*.csv
|
||||
2010.csv 2011.csv 2012.csv 2013.csv 2014.csv 2015.csv 2016.csv 2017.csv 2018.csv 2019.csv
|
||||
|
||||
```
|
||||
### 2\. Extract average temperatures
|
||||
|
||||
Extract the TAVG (average temperature) data from the CSVs for US regions:
|
||||
|
||||
**extract_tavg_us.sh**
|
||||
```
|
||||
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
# For each file with name that starts with "20" and ens with ".csv"
|
||||
for csv_file in `ls 20*.csv`
|
||||
do
|
||||
# Message that says file name $csv_file is extracted to file TAVG_US_$csv_file
|
||||
# Example: 2010.csv extracted to TAVG_US_2010.csv
|
||||
echo "$csv_file -> TAVG_US_$csv_file"
|
||||
# grep "TAVG" $csv_file: Extract lines in file with text "TAVG"
|
||||
# |: pipe
|
||||
# grep "^US": From those extract lines that begin with text "US"
|
||||
# > TAVG_US_$csv_file: Save xtracted lines to file TAVG_US_$csv_file
|
||||
grep "TAVG" $csv_file | grep "^US" > TAVG_US_$csv_file
|
||||
done
|
||||
|
||||
```
|
||||
This script:
|
||||
```
|
||||
|
||||
|
||||
$ ./extract_tavg_us.sh
|
||||
2010.csv -> TAVG_US_2010.csv
|
||||
...
|
||||
2019.csv -> TAVG_US_2019.csv
|
||||
|
||||
```
|
||||
creates these files:
|
||||
```
|
||||
|
||||
|
||||
$ ls TAVG_US*.csv
|
||||
TAVG_US_2010.csv TAVG_US_2011.csv TAVG_US_2012.csv TAVG_US_2013.csv
|
||||
TAVG_US_2014.csv TAVG_US_2015.csv TAVG_US_2016.csv TAVG_US_2017.csv
|
||||
TAVG_US_2018.csv TAVG_US_2019.csv
|
||||
|
||||
```
|
||||
Here are the first few lines for `TAVG_US_2010.csv`:
|
||||
```
|
||||
|
||||
|
||||
$ head TAVG_US_2010.csv
|
||||
USR0000AALC,20100101,TAVG,-220,,,U,
|
||||
USR0000AALP,20100101,TAVG,-9,,,U,
|
||||
USR0000ABAN,20100101,TAVG,12,,,U,
|
||||
USR0000ABCA,20100101,TAVG,16,,,U,
|
||||
USR0000ABCK,20100101,TAVG,-309,,,U,
|
||||
USR0000ABER,20100101,TAVG,-81,,,U,
|
||||
USR0000ABEV,20100101,TAVG,-360,,,U,
|
||||
USR0000ABEN,20100101,TAVG,-224,,,U,
|
||||
USR0000ABNS,20100101,TAVG,89,,,U,
|
||||
USR0000ABLA,20100101,TAVG,59,,,U,
|
||||
|
||||
```
|
||||
The [head][21] command is a utility for displaying the first several lines (by default, 10 lines) of a file.
|
||||
|
||||
The data has more information than you need. Limit the number of columns by eliminating column 3 (since all the data is average temperature) and column 5 onward. In other words, keep columns 1 (climate station), 2 (date), and 4 (temperature recorded).
|
||||
|
||||
**key_columns.sh**
|
||||
```
|
||||
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
# For each file with name that starts with "TAVG_US_" and ens with ".csv"
|
||||
for csv_file in `ls TAVG_US_*.csv`
|
||||
do
|
||||
echo "Exractiing columns $csv_file"
|
||||
# cat $csv_file: 'cat' is to con'cat'enate files - here used to show one year csv file
|
||||
# |: pipe
|
||||
# cut -d',' -f1,2,4: Cut columns 1,2,4 with , delimitor
|
||||
# > $csv_file.cut: Save to temporary file
|
||||
| > $csv_file.cut:
|
||||
cat $csv_file | cut -d',' -f1,2,4 > $csv_file.cut
|
||||
# mv $csv_file.cut $csv_file: Rename temporary file to original file
|
||||
mv $csv_file.cut $csv_file
|
||||
# File is processed and saved back into the same
|
||||
# There are other ways to do this
|
||||
# Using intermediate file is the most reliable method.
|
||||
done
|
||||
|
||||
```
|
||||
Run the script:
|
||||
```
|
||||
|
||||
|
||||
$ ./key_columns.sh
|
||||
Extracting columns TAVG_US_2010.csv
|
||||
...
|
||||
Extracting columns TAVG_US_2019.csv
|
||||
|
||||
```
|
||||
The first few lines of `TAVG_US_2010.csv` with the unneeded data removed are:
|
||||
```
|
||||
|
||||
|
||||
$ head TAVG_US_2010.csv
|
||||
USR0000AALC,20100101,-220
|
||||
USR0000AALP,20100101,-9
|
||||
USR0000ABAN,20100101,12
|
||||
USR0000ABCA,20100101,16
|
||||
USR0000ABCK,20100101,-309
|
||||
USR0000ABER,20100101,-81
|
||||
USR0000ABEV,20100101,-360
|
||||
USR0000ABEN,20100101,-224
|
||||
USR0000ABNS,20100101,89
|
||||
USR0000ABLA,20100101,59
|
||||
|
||||
```
|
||||
Dates are in string form (YMD). To train your model correctly, your algorithms need to recognize date fields in the comma-separated Y,M,D form (For example, `20100101` becomes `2010,01,01`). You can convert them with the [sed][22] utility.
|
||||
|
||||
**date_format.sh**
|
||||
```
|
||||
|
||||
|
||||
for csv_file in `ls TAVG_*.csv`
|
||||
do
|
||||
echo Date formatting $csv_file
|
||||
# This inserts , after year
|
||||
sed -i 's/,..../&,/' $csv_file
|
||||
# This inserts , after month
|
||||
sed -i 's/,....,../&,/' $csv_file
|
||||
done
|
||||
|
||||
```
|
||||
Run the script:
|
||||
```
|
||||
|
||||
|
||||
$ ./date_format.sh
|
||||
Date formatting TAVG_US_2010.csv
|
||||
...
|
||||
Date formatting TAVG_US_2019.csv
|
||||
|
||||
```
|
||||
The first few lines of `TAVG_US_2010.csv` with the comma-separated date format are:
|
||||
```
|
||||
|
||||
|
||||
$ head TAVG_US_2010.csv
|
||||
USR0000AALC,2010,01,01,-220
|
||||
USR0000AALP,2010,01,01,-9
|
||||
USR0000ABAN,2010,01,01,12
|
||||
USR0000ABCA,2010,01,01,16
|
||||
USR0000ABCK,2010,01,01,-309
|
||||
USR0000ABER,2010,01,01,-81
|
||||
USR0000ABEV,2010,01,01,-360
|
||||
USR0000ABEN,2010,01,01,-224
|
||||
USR0000ABNS,2010,01,01,89
|
||||
USR0000ABLA,2010,01,01,59
|
||||
|
||||
```
|
||||
### 3\. Aggregate states' average temperature data
|
||||
|
||||
The weather data comes from climate stations located in US cities, but you want to forecast whole states' temperatures. To convert the climate-station data to state data, first, map climate stations to their states.
|
||||
|
||||
Download the list of climate stations using wget:
|
||||
```
|
||||
`$ wget ftp://ftp.ncdc.noaa.gov/pub/data/ghcn/daily/ghcnd-stations.txt`
|
||||
```
|
||||
Extract the US stations with the [grep][23] utility to find US listings. The following command searches for lines that begin with the text `"US`." The `>` is a [redirection][24] that writes output to a file—in this case, to a file named `us_stations.txt`:
|
||||
```
|
||||
`$ grep "^US" ghcnd-stations.txt > us_stations.txt`
|
||||
```
|
||||
This file was created for pretty print, so the column separators are inconsistent:
|
||||
```
|
||||
|
||||
|
||||
$ head us_stations.txt
|
||||
US009052008 43.7333 -96.6333 482.0 SD SIOUX FALLS (ENVIRON. CANADA)
|
||||
US10RMHS145 40.5268 -105.1113 1569.1 CO RMHS 1.6 SSW
|
||||
US10adam001 40.5680 -98.5069 598.0 NE JUNIATA 1.5 S
|
||||
...
|
||||
|
||||
```
|
||||
Make them consistent by using [cat][25] to print the file, using [tr][26] to squeeze repeats and output to a temp file, and renaming the temp file back to the original—all in one line:
|
||||
```
|
||||
`$ cat us_stations.txt | tr -s ' ' > us_stations.txt.tmp; cp us_stations.txt.tmp us_stations.txt;`
|
||||
```
|
||||
The first lines of the command's output:
|
||||
```
|
||||
|
||||
|
||||
$ head us_stations.txt
|
||||
US009052008 43.7333 -96.6333 482.0 SD SIOUX FALLS (ENVIRON. CANADA)
|
||||
US10RMHS145 40.5268 -105.1113 1569.1 CO RMHS 1.6 SSW
|
||||
US10adam001 40.5680 -98.5069 598.0 NE JUNIATA 1.5 S
|
||||
...
|
||||
|
||||
```
|
||||
This contains a lot of info—GPS coordinates and such—but you only need the station code and state. Use [cut][27]:
|
||||
```
|
||||
`$ cut -d' ' -f1,5 us_stations.txt > us_stations.txt.tmp; mv us_stations.txt.tmp us_stations.txt;`
|
||||
```
|
||||
The first lines of the command's output:
|
||||
```
|
||||
|
||||
|
||||
$ head us_stations.txt
|
||||
US009052008 SD
|
||||
US10RMHS145 CO
|
||||
US10adam001 NE
|
||||
US10adam002 NE
|
||||
...
|
||||
|
||||
```
|
||||
Make this a CSV and change the spaces to comma separators using sed:
|
||||
```
|
||||
`$ sed -i s/' '/,/g us_stations.txt`
|
||||
```
|
||||
The first lines of the command's output:
|
||||
```
|
||||
|
||||
|
||||
$ head us_stations.txt
|
||||
US009052008,SD
|
||||
US10RMHS145,CO
|
||||
US10adam001,NE
|
||||
US10adam002,NE
|
||||
...
|
||||
|
||||
```
|
||||
Although you used several commands for these tasks, it is possible to perform all the steps in one run. Try it yourself.
|
||||
|
||||
Now, replace the station codes with their state locations by using [AWK][28], which is functionally high performant for large data processing.
|
||||
|
||||
**station_to_state_data.sh**
|
||||
```
|
||||
|
||||
|
||||
PATTERN_FILE=us_stations.txt
|
||||
|
||||
for DATA_FILE in `ls TAVG_US_*.csv`
|
||||
do
|
||||
echo ${DATA_FILE}
|
||||
|
||||
awk -F, \
|
||||
'FNR==NR { x[$1]=$2; next; } { $1=x[$1]; print $0 }' \
|
||||
OFS=, \
|
||||
${PATTERN_FILE} ${DATA_FILE} > ${DATA_FILE}.tmp
|
||||
|
||||
mv ${DATA_FILE}.tmp ${DATA_FILE}
|
||||
done
|
||||
|
||||
```
|
||||
Here is what these parameters mean:
|
||||
|
||||
`-F,` | Field separator is `,`
|
||||
---|---
|
||||
`FNR` | Line number in each file
|
||||
`NR` | Line number in both files together
|
||||
`FNR==NR` | Is TRUE only in the first file `${PATTERN_FILE}`
|
||||
`{ x[$1]=$2; next; }` | If `FNR==NR` is TRUE (for all lines in `$PATTERN_FILE` only)
|
||||
`- x` | Variable to store `station=state` map
|
||||
`- x[$1]=$2` | Adds data of `station=state` to map
|
||||
`- $1` | First column in first file (station codes)
|
||||
`- $2` | Second column in first file (state codes)
|
||||
`- x` | Map of all stations e.g., `x[US009052008]=SD`, `x[US10RMHS145]=CO`, ..., `x[USW00096409]=AK`
|
||||
`- next` | Go to next line matching `FNR==NR` (essentially, this creates a map of all stations-states from the `${PATTERN_FILE}`
|
||||
`{ $1=x[$1]; print $0 }` | If `FNR==NR` is FALSE (for all lines `in $DATA_FILE` only)
|
||||
`- $1=x[$1]` | Replace first field with `x[$1]`; essentially, replace station code with state code
|
||||
`- print $0` | Print all columns (including replaced `$1`)
|
||||
`OFS=,` | Output fields separator is `,`
|
||||
|
||||
The CSV with station codes:
|
||||
```
|
||||
|
||||
|
||||
$ head TAVG_US_2010.csv
|
||||
USR0000AALC,2010,01,01,-220
|
||||
USR0000AALP,2010,01,01,-9
|
||||
USR0000ABAN,2010,01,01,12
|
||||
USR0000ABCA,2010,01,01,16
|
||||
USR0000ABCK,2010,01,01,-309
|
||||
USR0000ABER,2010,01,01,-81
|
||||
USR0000ABEV,2010,01,01,-360
|
||||
USR0000ABEN,2010,01,01,-224
|
||||
USR0000ABNS,2010,01,01,89
|
||||
USR0000ABLA,2010,01,01,59
|
||||
|
||||
```
|
||||
Run the command:
|
||||
```
|
||||
|
||||
|
||||
$ ./station_to_state_data.sh
|
||||
TAVG_US_2010.csv
|
||||
...
|
||||
TAVG_US_2019.csv
|
||||
|
||||
```
|
||||
Stations are now mapped to states:
|
||||
```
|
||||
|
||||
|
||||
$ head TAVG_US_2010.csv
|
||||
AK,2010,01,01,-220
|
||||
AZ,2010,01,01,-9
|
||||
AL,2010,01,01,12
|
||||
AK,2010,01,01,16
|
||||
AK,2010,01,01,-309
|
||||
AK,2010,01,01,-81
|
||||
AK,2010,01,01,-360
|
||||
AK,2010,01,01,-224
|
||||
AZ,2010,01,01,59
|
||||
AK,2010,01,01,-68
|
||||
|
||||
```
|
||||
Every state has several temperature readings for each day, so you need to calculate the average of each state's readings for a day. Use AWK for text processing, [sort][29] to ensure the final results are in a logical order, and [rm][30] to delete the temporary file after processing.
|
||||
|
||||
**station_to_state_data.sh**
|
||||
```
|
||||
|
||||
|
||||
PATTERN_FILE=us_stations.txt
|
||||
|
||||
for DATA_FILE in `ls TAVG_US_*.csv`
|
||||
do
|
||||
echo ${DATA_FILE}
|
||||
|
||||
awk -F, \
|
||||
'FNR==NR { x[$1]=$2; next; } { $1=x[$1]; print $0 }' \
|
||||
OFS=, \
|
||||
${PATTERN_FILE} ${DATA_FILE} > ${DATA_FILE}.tmp
|
||||
|
||||
mv ${DATA_FILE}.tmp ${DATA_FILE}
|
||||
done
|
||||
|
||||
```
|
||||
Here is what the AWK parameters mean:
|
||||
|
||||
`FILE=$DATA_FILE` | CSV file processed as `FILE`
|
||||
---|---
|
||||
`-F,` | Field separator is `,`
|
||||
`state_day_sum[$1 "," $2 "," $3 "," $4] = $5 state_day_sum[$1 "," $2 "," $3 "," $4] + $5` | Sum of temperature (`$5`) for the state `($1`) on year (`$2`), month (`$3`), day (`$4`)
|
||||
`state_day_num[$1 "," $2 "," $3 "," $4] = $5 state_day_num[$1 "," $2 "," $3 "," $4] + 1` | Number of temperature readings for the state (`$1`) on year (`$2`), month (`$3`), day (`$4`)
|
||||
`END` | In the end, after collecting sum and number of readings for all states, years, months, days, calculate averages
|
||||
`for (state_day_key in state_day_sum)` | For each state-year-month-day
|
||||
`print state_day_key "," state_day_sum[state_day_key]/state_day_num[state_day_key]` | Print state,year,month,day,average
|
||||
`OFS=,` | Output fields separator is `,`
|
||||
`$DATA_FILE` | Input file (all files with name starting with `TAVG_US_` and ending with `.csv`, one by one)
|
||||
`> STATE_DAY_${DATA_FILE}.tmp` | Save result to a temporary file
|
||||
|
||||
Run the script:
|
||||
```
|
||||
|
||||
|
||||
$ ./TAVG_avg.sh
|
||||
TAVG_US_2010.csv
|
||||
TAVG_US_2011.csv
|
||||
TAVG_US_2012.csv
|
||||
TAVG_US_2013.csv
|
||||
TAVG_US_2014.csv
|
||||
TAVG_US_2015.csv
|
||||
TAVG_US_2016.csv
|
||||
TAVG_US_2017.csv
|
||||
TAVG_US_2018.csv
|
||||
TAVG_US_2019.csv
|
||||
|
||||
```
|
||||
These files are created:
|
||||
```
|
||||
|
||||
|
||||
$ ls STATE_DAY_TAVG_US_20*.csv
|
||||
STATE_DAY_TAVG_US_2010.csv STATE_DAY_TAVG_US_2015.csv
|
||||
STATE_DAY_TAVG_US_2011.csv STATE_DAY_TAVG_US_2016.csv
|
||||
STATE_DAY_TAVG_US_2012.csv STATE_DAY_TAVG_US_2017.csv
|
||||
STATE_DAY_TAVG_US_2013.csv STATE_DAY_TAVG_US_2018.csv
|
||||
STATE_DAY_TAVG_US_2014.csv STATE_DAY_TAVG_US_2019.csv
|
||||
|
||||
```
|
||||
See one year of data for all states ([less][31] is a utility to see output a page at a time):
|
||||
```
|
||||
|
||||
|
||||
$ less STATE_DAY_TAVG_US_2010.csv
|
||||
AK,2010,01,01,-181.934
|
||||
...
|
||||
AK,2010,01,31,-101.068
|
||||
AK,2010,02,01,-107.11
|
||||
...
|
||||
AK,2010,02,28,-138.834
|
||||
...
|
||||
WY,2010,01,01,-43.5625
|
||||
...
|
||||
WY,2010,12,31,-215.583
|
||||
|
||||
```
|
||||
Merge all the data files into one:
|
||||
```
|
||||
`$ cat STATE_DAY_TAVG_US_20*.csv > TAVG_US_2010-2019.csv`
|
||||
```
|
||||
You now have one file, with all states, for all years:
|
||||
```
|
||||
|
||||
|
||||
$ cat TAVG_US_2010-2019.csv
|
||||
AK,2010,01,01,-181.934
|
||||
...
|
||||
WY,2018,12,31,-167.421
|
||||
AK,2019,01,01,-32.3386
|
||||
...
|
||||
WY,2019,12,30,-131.028
|
||||
WY,2019,12,31,-79.8704
|
||||
|
||||
```
|
||||
## 4\. Make time-series data
|
||||
|
||||
A problem like this is fittingly addressed with a time-series model such as long short-term memory ([LSTM][32]), which is a recurring neural network ([RNN][33]). This input data is organized into time slices; consider 20 days to be one slice.
|
||||
|
||||
This is a one-time slice (as in `STATE_DAY_TAVG_US_2010.csv`):
|
||||
```
|
||||
|
||||
|
||||
X (input – 20 weeks):
|
||||
AK,2010,01,01,-181.934
|
||||
AK,2010,01,02,-199.531
|
||||
...
|
||||
AK,2010,01,20,-157.273
|
||||
|
||||
y (21st week, prediction for these 20 weeks):
|
||||
AK,2010,01,21,-165.31
|
||||
|
||||
```
|
||||
This time slice is represented as (temperature values where the first 20 weeks are X, and 21 is y):
|
||||
```
|
||||
|
||||
|
||||
AK, -181.934,-199.531, ... ,
|
||||
-157.273,-165.3
|
||||
|
||||
```
|
||||
The slices are time-contiguous. For example, the end of 2010 continues into 2011:
|
||||
```
|
||||
|
||||
|
||||
AK,2010,12,22,-209.92
|
||||
...
|
||||
AK,2010,12,31,-79.8523
|
||||
AK,2011,01,01,-59.5658
|
||||
...
|
||||
AK,2011,01,10,-100.623
|
||||
|
||||
```
|
||||
Which results in the prediction:
|
||||
```
|
||||
`AK,2011,01,11,-106.851`
|
||||
```
|
||||
This time slice is taken as:
|
||||
```
|
||||
`AK, -209.92, ... ,-79.8523,-59.5658, ... ,-100.623,-106.851`
|
||||
```
|
||||
and so on, for all states, years, months, and dates. For more explanation, see this tutorial on [time-series forecasting][34].
|
||||
|
||||
Write a script to create time slices:
|
||||
|
||||
**timeslices.sh**
|
||||
```
|
||||
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
TIME_SLICE_PERIOD=20
|
||||
|
||||
file=TAVG_US_2010-2019.csv
|
||||
|
||||
# For each state in file
|
||||
for state in `cut -d',' -f1 $file | sort | uniq`
|
||||
do
|
||||
# Get all temperature values for the state
|
||||
state_tavgs=`grep $state $file | cut -d',' -f5`
|
||||
# How many time slices will this result in?
|
||||
# mber of temperatures recorded minus size of one timeslice
|
||||
num_slices=`echo $state_tavgs | wc -w`
|
||||
num_slices=$((${num_slices} - ${TIME_SLICE_PERIOD}))
|
||||
# Initialize
|
||||
slice_start=1; num_slice=0;
|
||||
# For each timeslice
|
||||
while [ $num_slice -lt $num_slices ]
|
||||
do
|
||||
# One timeslice is from slice_start to slice_end
|
||||
slice_end=$(($slice_start + $TIME_SLICE_PERIOD - 1))
|
||||
# X (1-20)
|
||||
sliceX="$slice_start-$slice_end"
|
||||
# y (21)
|
||||
slicey=$(($slice_end + 1))
|
||||
# Print state and timeslice temperature values (column 1-20 and 21)
|
||||
echo $state `echo $state_tavgs | cut -d' ' -f$sliceX,$slicey`
|
||||
# Increment
|
||||
slice_start=$(($slice_start + 1)); num_slice=$(($num_slice + 1));
|
||||
done
|
||||
done
|
||||
|
||||
```
|
||||
Run the script. It uses spaces as column separators; make them commas with sed:
|
||||
```
|
||||
`$ ./timeslices.sh > TIMESLICE_TAVG_US_2010-2019.csv; sed -i s/' '/,/g TIME_VARIANT_TAVG_US_2010-2019.csv`
|
||||
```
|
||||
Here are the first few lines and the last few lines of the output .csv:
|
||||
```
|
||||
|
||||
|
||||
$ head -3 TIME_VARIANT_TAVG_US_2009-2019.csv
|
||||
AK,-271.271,-290.057,-300.324,-277.603,-270.36,-293.152,-292.829,-270.413,-256.674,-241.546,-217.757,-158.379,-102.585,-24.9517,-1.7973,15.9597,-5.78231,-33.932,-44.7655,-92.5694,-123.338
|
||||
AK,-290.057,-300.324,-277.603,-270.36,-293.152,-292.829,-270.413,-256.674,-241.546,-217.757,-158.379,-102.585,-24.9517,-1.7973,15.9597,-5.78231,-33.932,-44.7655,-92.5694,-123.338,-130.829
|
||||
AK,-300.324,-277.603,-270.36,-293.152,-292.829,-270.413,-256.674,-241.546,-217.757,-158.379,-102.585,-24.9517,-1.7973,15.9597,-5.78231,-33.932,-44.7655,-92.5694,-123.338,-130.829,-123.979
|
||||
|
||||
$ tail -3 TIME_VARIANT_TAVG_US_2009-2019.csv
|
||||
WY,-76.9167,-66.2315,-45.1944,-27.75,-55.3426,-81.5556,-124.769,-137.556,-90.213,-54.1389,-55.9907,-30.9167,-9.59813,7.86916,-1.09259,-13.9722,-47.5648,-83.5234,-98.2963,-124.694,-142.898
|
||||
WY,-66.2315,-45.1944,-27.75,-55.3426,-81.5556,-124.769,-137.556,-90.213,-54.1389,-55.9907,-30.91
|
@ -0,0 +1,108 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Customize Task Switching Experience on GNOME Desktop With These Nifty Tools)
|
||||
[#]: via: (https://itsfoss.com/customize-gnome-task-switcher/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
Customize Task Switching Experience on GNOME Desktop With These Nifty Tools
|
||||
======
|
||||
|
||||
Unless you’re new to Linux, you know that there are several [popular desktop environment][1] choices for users. And if you’re that newbie, I recommend you to learn [what a desktop environment is][2] along with this tutorial.
|
||||
|
||||
Here, I shall be focusing on tweaking the task switching experience on GNOME. I know that the majority of users just tend to use it as is and stock settings are good enough for the most part.
|
||||
|
||||
I mean there is nothing wrong with the application switcher that [you use with Alt+Tab keyboard shortcut in Ubuntu][3].
|
||||
|
||||
![][4]
|
||||
|
||||
However, if you are a tinkerer who wants to [customize the look and feel of your GNOME desktop][5], including the task switcher and animation effects when launching or minimizing an app — you might want to continue reading this article.
|
||||
|
||||
### Change GNOME Task Switcher to Windows 7 Style Effect
|
||||
|
||||
![][6]
|
||||
|
||||
Switching between running applications using the key bind **Alt+Tab** is fast but it may not be the most intuitive experience for some. You just get to cycle through a bunch of icons depending on the number of active applications.
|
||||
|
||||
What if you want to change how the task switcher looks?
|
||||
|
||||
Well, you can easily give it a look of Windows 7 Aero Flip 3D effect. And, here’s how it will look:
|
||||
|
||||
![][7]
|
||||
|
||||
It definitely looks interesting to have a different task switcher. Why? Just for fun or to share your desktop’s screenshot on Linux communities.
|
||||
|
||||
Now, to get this on your GNOME desktop, here’s what you have to do:
|
||||
|
||||
Step 1: Enable GNOME extensions if you haven’t already. You can follow our guide to [learn how to use GNOME shell extensions][8].
|
||||
|
||||
Step 2: Once you are done with the setup, you can proceed downloading and installing the [Coverflow GNOME extension][9] from GNOME extensions website.
|
||||
|
||||
In case you haven’t installed the browser extension, you can just click on the link “**Click here to install browser extension**” from the notice as shown in the screenshot below.
|
||||
|
||||
![][10]
|
||||
|
||||
Step 3: Next, you just have to refresh the web page and enable the extension as shown in the screenshot below.
|
||||
|
||||
![][11]
|
||||
|
||||
You also get some customization options if you click on the “gear” icon right to the toggle button.
|
||||
|
||||
![][12]
|
||||
|
||||
Of course, depending on how fast you want it to be or how good you want it to look, you will have to adjust the animation speed accordingly.
|
||||
|
||||
Next, why not some kind of cool effect when you interact with applications (minimize/close)? I have just the solution for you.
|
||||
|
||||
### Add Genie Animation Effect While Minimizing & Re-opening Applications
|
||||
|
||||
There’s an interesting effect (sort of like genie popping out of a lamp) that you can add to see when you minimize or re-open an app.
|
||||
|
||||
This also comes as a GNOME extension, so you do not need to do anything else to get started.
|
||||
|
||||
You just have to head to the extensions page, which is [Compiz alike Magic Lamp effect][13] and then enable the extension to see it in action.
|
||||
|
||||
![][14]
|
||||
|
||||
Here’s how it looks in action:
|
||||
|
||||
![][15]
|
||||
|
||||
It would look even cooler if you switch the Ubuntu dock to the bottom.
|
||||
|
||||
Exciting GNOME extensions, right? You can play around to tweak your GNOME experience using the [GNOME tweaks app][16] and [install some beautiful icon themes][17] or explore different options.
|
||||
|
||||
How do you prefer to customize your GNOME experience? Is there any other cool GNOME extension or an app that you tend to utilize? Feel free to share your thoughts in the comments below.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/customize-gnome-task-switcher/
|
||||
|
||||
作者:[Ankush Das][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/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://itsfoss.com/best-linux-desktop-environments/
|
||||
[2]: https://itsfoss.com/what-is-desktop-environment/
|
||||
[3]: https://itsfoss.com/ubuntu-shortcuts/
|
||||
[4]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2017/10/gnome-app-switcher.jpeg?resize=800%2C255&ssl=1
|
||||
[5]: https://itsfoss.com/gnome-tricks-ubuntu/
|
||||
[6]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/11/ubuntu-coverflow-screenshot.jpg?resize=800%2C387&ssl=1
|
||||
[7]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/11/coverflow-task-switcher.jpg?resize=800%2C392&ssl=1
|
||||
[8]: https://itsfoss.com/gnome-shell-extensions/
|
||||
[9]: https://extensions.gnome.org/extension/97/coverflow-alt-tab/
|
||||
[10]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/11/gnome-shell-extension-browser.jpg?resize=800%2C401&ssl=1
|
||||
[11]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/11/coverflow-enable.jpg?resize=800%2C303&ssl=1
|
||||
[12]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/11/coverflow-settings.png?resize=800%2C481&ssl=1
|
||||
[13]: https://extensions.gnome.org/extension/3740/compiz-alike-magic-lamp-effect/
|
||||
[14]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/11/magic-lamp-extension.jpg?resize=800%2C355&ssl=1
|
||||
[15]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/11/magic-lamp-effect-800x380.gif?resize=800%2C380&ssl=1
|
||||
[16]: https://itsfoss.com/gnome-tweak-tool/
|
||||
[17]: https://itsfoss.com/best-icon-themes-ubuntu-16-04/
|
@ -0,0 +1,126 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (5 new sudo features you need to know in 2020)
|
||||
[#]: via: (https://opensource.com/article/20/10/sudo-19)
|
||||
[#]: author: (Peter Czanik https://opensource.com/users/czanik)
|
||||
|
||||
2020 年的 5 个 新 sudo 功能
|
||||
======
|
||||
|
||||
> 从通过 chroot 支持集中会话录制到 Python API,sudo 1.9 提供了许多新功能。
|
||||
|
||||
![Wratchet set tools][1] 。
|
||||
|
||||
当你想在 [POSIX 系统][2]上执行一个操作时,最安全的方法之一就是使用 `sudo` 命令。与以 root 用户身份登录并执行命令可能是个危险的操作不同,`sudo` 授予任何被系统管理员[指定为 “sudoer”][3]的用户临时权限来执行通常受限制的活动。
|
||||
|
||||
几十年来,这个系统帮助 Linux、Unix 和 macOS 系统免受愚蠢的错误和恶意攻击,它是当今所有主要 Linux 发行版的默认管理机制。
|
||||
|
||||
当在 2020 年 5 月发布 sudo 1.9 时,它带来了许多新功能,包括集中收集会话记录,支持 `sudo` 内的 chroot,以及 Python API。如果你对其中的任何一项感到惊讶,请阅读我的文章,了解一些 [sudo 鲜为人知的功能][4]。
|
||||
|
||||
`sudo` 不仅仅是一个管理命令的前缀。你可以微调权限,记录终端上发生的事情,使用插件扩展`sudo`,在 LDAP 中存储配置,进行广泛的日志记录,以及更多。
|
||||
|
||||
1.9.0 版本和后续的小版本增加了各种新功能(我将在下面介绍),包括:
|
||||
|
||||
* 一个集中收集 `sudo` 会话记录的记录服务
|
||||
* 审计插件 API
|
||||
* 审批插件 API
|
||||
* Python 对插件的支持
|
||||
* `sudo` 内置 chroot 和 CWD 支持(从 1.9.3 开始)
|
||||
|
||||
### 哪里可以得到 sudo 1.9?
|
||||
|
||||
大多数的 Linux 发行版仍然封装了上一代的 `sudo`(1.8 版本),并且在长期支持(LTS)的发行版中会保持这种方式数年。据我所知,提供了最完整的 sudo 1.9 包的 Linux 发行版是 openSUSE[Tumbleweed][5],它是一个滚动发行版,而且该 `sudo` 包的子包中有 Python 支持。最近的 [Fedora][6] 版本包含了 sudo 1.9,但没有 Python。[FreeBSD Ports][7] 有最新的 `sudo` 版本,如果你自己编译 `sudo` 而不是使用软件包,你可以启用 Python 支持。
|
||||
|
||||
如果你喜欢的 Linux 发行版还没有包含 sudo 1.9,请查看 [sudo 二进制页面][8]来查看是否有现成的包可以用于你的系统。这个页面还提供了一些商用 Unix 变种的软件包。
|
||||
|
||||
像往常一样,在你开始试验 `sudo` 设置之前,*确保你知道 root 密码*。是的,即使在 Ubuntu 上也是如此。有一个临时的“后门”是很重要的;如果没有这个后门,如果出了问题,你就必须得黑掉自己的系统。记住:语法正确的配置并不意味着任何人都可以在该系统上通过 `sudo` 做任何事情!
|
||||
|
||||
### 记录服务
|
||||
|
||||
记录服务可以集中收集会话记录。与本地会话记录存储相比,这有很多优势:
|
||||
|
||||
* 更方便地在一个地方进行搜索,而不是访问各个机器来寻找记录
|
||||
* 即使在发送机器停机的情况下也可以进行记录
|
||||
* 本地用户若想掩盖其轨迹,不能删除记录
|
||||
|
||||
为了快速测试,你可以通过非加密连接向记录服务发送会话。我的博客中包含了[说明][9],可以在几分钟内完成设置。对于生产环境,我建议使用加密连接。有很多可能性,所以阅读最适合你的环境的[文档][10]。
|
||||
|
||||
### 审计插件 API
|
||||
|
||||
新的审计插件 API 不是一个用户可见的功能。换句话说,你不能从 `sudoers` 文件中配置它。它是一个 API,意味着你可以从插件中访问审计信息,包括用 Python 编写的插件。你可以用很多不同的方式来使用它,比如当一些有趣的事情发生时,从 `sudo` 直接发送事件到 Elasticsearch 或日志即服务(LaaS)。你也可以用它来进行调试,并以任何你喜欢的格式将其他难以访问的信息打印到屏幕上。
|
||||
|
||||
根据你使用它的方式,你可以在 `sudo` 插件手册页(针对 C 语言)和 `sudo` Python 插件手册中找到它的文档。在 `sudo` 源代码中可以找到 [Python 代码示例][11],在我的博客上也有一个[简化的例子][12]。
|
||||
|
||||
### 审批插件 API
|
||||
|
||||
审批插件 API 可以在命令执行之前加入额外的限制。这些限制只有在策略插件成功后才会运行,因此你可以有效地添加额外的策略层,而无需更换策略插件,进而无需更换 `sudoers`。可以定义多个审批插件,而且所有插件都必须成功,命令才能执行。
|
||||
|
||||
与审计插件 API 一样,你可以从 C 和 Python 中使用它。我博客上记录的[示例 Python 代码][13]是对该 API 的一个很好的介绍。一旦你理解了它是如何工作的,你就可以扩展它来连接 `sudo` 到工单系统,并且只批准有相关开放工单的会话。你也可以连接到人力资源数据库,这样只有当班的工程师才能获得管理权限。
|
||||
|
||||
### Python 对插件的支持
|
||||
|
||||
尽管我不是程序员,但我最喜欢的 sudo 1.9 新特性是 Python 对插件的支持。你可以用 Python 也能使用 C 语言调用大部分 API。幸运的是,`sudo` 对性能不敏感,所以运行速度相对较慢的 Python 代码对 `sudo` 来说不是问题。使用 Python 来扩展 `sudo` 有很多优势:
|
||||
|
||||
* 更简单、更快速的开发
|
||||
* 不需要编译;甚至可以通过配置管理分发代码
|
||||
* 许多 API 没有现成的 C 客户端,但有 Python 代码
|
||||
|
||||
除了审计和审批插件 API 之外,还有一些其他的 API,你可以用它们做一些非常有趣的事情。
|
||||
|
||||
通过使用策略插件 API,你可以取代 `sudo` 策略引擎。请注意,你将失去大部分的 `sudo` 功能,而且没有更多基于 `sudoers` 的配置。这在小众情况下还是很有用的,但大多数时候,最好还是继续使用 `sudoers`,并使用审批插件 API 创建额外的策略。如果你想尝试一下,我的 [Python 插件介绍][14]提供了一个非常简单的策略:只允许使用 `id` 命令。再次确认你知道 root 密码,因为一旦启用这个策略,它就会阻止任何实际使用 `sudo` 的行为。
|
||||
|
||||
使用 I/O 日志 API,你可以访问用户会话的输入和输出。这意味着你可以分析会话中发生了什么,甚至在发现可疑情况时终止会话。这个 API 有很多可能的用途,比如防止数据泄露。你可以监控屏幕上的关键字,如果数据流中出现任何关键字,你可以在关键字出现在用户的屏幕上之前中断连接。另一种可能是检查用户正在输入的内容,并使用这些数据来重建用户正在输入的命令行。例如,如果用户输入 `rm -fr /`,你可以在按下回车键之前就断开用户的连接。
|
||||
|
||||
组插件 API 允许非 Unix 组的查找。在某种程度上,这与审批插件 API 类似,因为它也扩展了策略插件。你可以检查一个用户是否属于一个给定的组,并在后面的配置部分基于此采取行动。
|
||||
|
||||
### chroot 和 CWD 支持
|
||||
|
||||
`sudo` 的最新功能是支持 chroot 和改变工作目录(CWD),这两个选项都不是默认启用的,你需要在 `sudoers` 文件中明确启用它们。当它们被启用时,你可以微调目标目录或允许用户指定使用哪个目录。日志反映了这些设置何时被使用。
|
||||
|
||||
在大多数系统中,chroot 只对 root 用户开放。如果你的某个用户需要 chroot,你需要给他们 root 权限,这比仅仅给他们 chroot 权限要大得多。另外,你可以通过 `sudo` 允许访问 chroot 命令,但它仍然允许漏洞,他们可以获得完全的权限。当你使用 `sudo` 内置的 chroot 支持时,你可以轻松地限制对单个目录的访问。你也可以让用户灵活地指定根目录。当然,这可能会导致灾难(例如,`sudo --chroot / -s`),但至少事件会被记录下来。
|
||||
|
||||
当你通过 `sudo` 运行一个命令时,它会将工作目录设置为当前目录。这是预期的行为,但可能有一些情况下,命令需要在不同的目录下运行。例如,我记得使用一个应用程序,它通过检查我的工作目录是否是 `/root` 来检查我的权限。
|
||||
|
||||
### 尝试新功能
|
||||
|
||||
希望这篇文章能启发你仔细研究一下 sudo 1.9。集中会话记录比在本地存储会话日志更加方便和安全。chroot 和 CWD 支持为你提供了额外的安全性和灵活性。而使用 Python 来扩展 `sudo`,可以很容易地根据你的环境来定制 `sudo`。你可以通过使用最新的 Linux 发行版或 `sudo` 网站上的即用型软件包来尝试这些新功能。
|
||||
|
||||
如果你想了解更多关于 sudo 的信息,这里有一些资源:
|
||||
|
||||
* [Sudo 官网][15]
|
||||
* [Sudo 博客][16]
|
||||
* [Sudo on Twitter][17]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/10/sudo-19
|
||||
|
||||
作者:[Peter Czanik][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/czanik
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/tools_osyearbook2016_sysadmin_cc.png?itok=Y1AHCKI4 (Wratchet set tools)
|
||||
[2]: https://opensource.com/article/19/7/what-posix-richard-stallman-explains
|
||||
[3]: https://opensource.com/article/17/12/using-sudo-delegate
|
||||
[4]: https://opensource.com/article/19/10/know-about-sudo
|
||||
[5]: https://software.opensuse.org/distributions/tumbleweed
|
||||
[6]: https://getfedora.org/
|
||||
[7]: https://www.freebsd.org/ports/
|
||||
[8]: https://www.sudo.ws/download.html#binary
|
||||
[9]: https://blog.sudo.ws/posts/2020/03/whats-new-in-sudo-1.9-recording-service/
|
||||
[10]: https://www.sudo.ws/man/sudo_logsrvd.man.html#EXAMPLES
|
||||
[11]: https://github.com/sudo-project/sudo/blob/master/plugins/python/example_audit_plugin.py
|
||||
[12]: https://blog.sudo.ws/posts/2020/06/sudo-1.9-using-the-new-audit-api-from-python/
|
||||
[13]: https://blog.sudo.ws/posts/2020/08/sudo-1.9-using-the-new-approval-api-from-python/
|
||||
[14]: https://blog.sudo.ws/posts/2020/01/whats-new-in-sudo-1.9-python/
|
||||
[15]: https://www.sudo.ws/
|
||||
[16]: https://blog.sudo.ws/
|
||||
[17]: https://twitter.com/sudoproject
|
@ -0,0 +1,93 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (LazPaint: A Free & Open Source Paint.NET Alternative)
|
||||
[#]: via: (https://itsfoss.com/lazpaint/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
LazPaint:一个免费和开源的 Paint.NET 替代品
|
||||
======
|
||||
|
||||
_**简介:LazPaint 是一款开源 Paint.NET 替代品,支持跨平台。它是一个轻量级的程序,拥有一堆快速编辑图像的基本选项。下面是 LazPaint 的概述。**_
|
||||
|
||||
### LazPaint:Linux 下的开源 Paint.NET 替代品
|
||||
|
||||
![][1]
|
||||
|
||||
如果你喜欢使用工具来快速编辑和处理图像和截图,你可能听说过 [Paint.NET][2],它只适用于 Windows 系统。
|
||||
|
||||
它是一个流行的小工具,可以完成很多基本的编辑任务,同时还有一堆可用的选项。你可能知道一些[图像编辑工具][3],但 Paint.NET 是一个相当受欢迎的选择,因为对于一个普通用户来说,它易于使用,且没有任何臃肿的功能。
|
||||
|
||||
[LazPaint][4]成为了 Linux、Windows 和 macOS 上 Paint.NET 的令人印象深刻的开源替代品。它提供了操作图像所需的大部分基本功能,同时又易于使用。
|
||||
|
||||
![][5]
|
||||
|
||||
由于它是跨平台的应用,即使你不使用 Linux 系统,你仍然可以使用它作为免费的开源工具。 现在,让我们看一下它提供的一些功能。
|
||||
|
||||
### LazPaint 的功能
|
||||
|
||||
![][6]
|
||||
|
||||
正如我前面提到的,LazPaint 提供了一堆必要的功能。在这里,我将列出关键的部分,这可以帮助你决定是否需要它。然而,我建议你去探索它,以了解更多关于它的信息。
|
||||
|
||||
* 支持所有主要的文件格式(包括分层 Bitmap 和 3D 文件)
|
||||
* 选区工具、裁剪选区、选区笔、反转选区。
|
||||
* 支持导出到 [Krita][7]。
|
||||
* 用各种质量设置对图像进行重新采样
|
||||
* 运动模糊、自定义模糊、径向模糊和像素化工具。
|
||||
* 可以移除透明度和扁平化图像。
|
||||
* 旋转和翻转图像
|
||||
* 将图像转换为底片
|
||||
* 能够调整画布的大小
|
||||
* 变形工具(透视)
|
||||
* 高级绘图工具
|
||||
* 设置工作区颜色
|
||||
* 黑暗主题
|
||||
* 支持脚本功能
|
||||
* 具有基本管理选项的层支持
|
||||
* 层效果
|
||||
* 过滤器
|
||||
* 灰度效果
|
||||
* 能够启用/禁用工具栏或将其添加到 Dock 上
|
||||
|
||||
### 在 Linux 上安装 LazPaint
|
||||
|
||||
你应该可以在你的官方软件仓库中找到它,通过你的默认软件包管理器来安装它,但要获得最新版本,你需要下载 .**deb** 文件,或者在非 Debian 的发行版上从源码编译它。
|
||||
|
||||
我希望有一个 Flatpak 可以在每个 Linux 发行版上获得最新的版本,但目前还没有。
|
||||
|
||||
它也适用于 Windows 和 macOS。你会发现 Windows 还有一个便携版,可以派上用场。
|
||||
|
||||
[LazPaint][4]
|
||||
|
||||
### 关于 LazPaint 的总结
|
||||
|
||||
我发现它真的很容易使用,各种质量设置来重新采样(或调整大小)图像绝对是一个加分。如果你已经安装了它,你一定注意到,它不需要大量的存储空间,总体上是一个轻量级的程序。
|
||||
|
||||
它的速度很快,我在快速测试中的大部分功能都相当好用,没有任何问题。
|
||||
|
||||
你觉得 LazPaint 作为 Paint.NET 的替代品怎么样?请在下面的评论中告诉我你的想法。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/lazpaint/
|
||||
|
||||
作者:[Ankush Das][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://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/11/lazpaint.jpg?resize=800%2C397&ssl=1
|
||||
[2]: https://www.getpaint.net
|
||||
[3]: https://itsfoss.com/image-applications-ubuntu-linux/
|
||||
[4]: https://lazpaint.github.io/
|
||||
[5]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/11/lazpaint-screenshot-2.png?resize=800%2C481&ssl=1
|
||||
[6]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/11/lazpaint-screenshot.jpg?resize=800%2C484&ssl=1
|
||||
[7]: https://krita.org/en
|
||||
[9]: https://itsfoss.com/drawing-app/
|
Loading…
Reference in New Issue
Block a user